diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /library/std/src/sys/unix/os | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | library/std/src/sys/unix/os.rs | 680 | ||||
-rw-r--r-- | library/std/src/sys/unix/os/tests.rs | 23 | ||||
-rw-r--r-- | library/std/src/sys/unix/os_str.rs | 266 | ||||
-rw-r--r-- | library/std/src/sys/unix/os_str/tests.rs | 10 |
4 files changed, 979 insertions, 0 deletions
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs new file mode 100644 index 000000000..46545a083 --- /dev/null +++ b/library/std/src/sys/unix/os.rs @@ -0,0 +1,680 @@ +//! Implementation of `std::os` functionality for unix systems + +#![allow(unused_imports)] // lots of cfg code here + +#[cfg(test)] +mod tests; + +use crate::os::unix::prelude::*; + +use crate::error::Error as StdError; +use crate::ffi::{CStr, CString, OsStr, OsString}; +use crate::fmt; +use crate::io; +use crate::iter; +use crate::mem; +use crate::path::{self, PathBuf}; +use crate::ptr; +use crate::slice; +use crate::str; +use crate::sys::cvt; +use crate::sys::fd; +use crate::sys::memchr; +use crate::sys_common::rwlock::{StaticRwLock, StaticRwLockReadGuard}; +use crate::vec; + +#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] +use crate::sys::weak::weak; + +use libc::{c_char, c_int, c_void}; + +const TMPBUF_SZ: usize = 128; + +cfg_if::cfg_if! { + if #[cfg(target_os = "redox")] { + const PATH_SEPARATOR: u8 = b';'; + } else { + const PATH_SEPARATOR: u8 = b':'; + } +} + +extern "C" { + #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] + #[cfg_attr( + any( + target_os = "linux", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re" + ), + link_name = "__errno_location" + )] + #[cfg_attr( + any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "android", + target_os = "redox", + target_env = "newlib" + ), + link_name = "__errno" + )] + #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] + #[cfg_attr( + any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"), + link_name = "__error" + )] + #[cfg_attr(target_os = "haiku", link_name = "_errnop")] + fn errno_location() -> *mut c_int; +} + +/// Returns the platform-specific value of errno +#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] +pub fn errno() -> i32 { + unsafe { (*errno_location()) as i32 } +} + +/// Sets the platform-specific value of errno +#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall! +#[allow(dead_code)] // but not all target cfgs actually end up using it +pub fn set_errno(e: i32) { + unsafe { *errno_location() = e as c_int } +} + +#[cfg(target_os = "vxworks")] +pub fn errno() -> i32 { + unsafe { libc::errnoGet() } +} + +#[cfg(target_os = "dragonfly")] +pub fn errno() -> i32 { + extern "C" { + #[thread_local] + static errno: c_int; + } + + unsafe { errno as i32 } +} + +#[cfg(target_os = "dragonfly")] +#[allow(dead_code)] +pub fn set_errno(e: i32) { + extern "C" { + #[thread_local] + static mut errno: c_int; + } + + unsafe { + errno = e; + } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(errno: i32) -> String { + extern "C" { + #[cfg_attr(any(target_os = "linux", target_env = "newlib"), link_name = "__xpg_strerror_r")] + fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; + } + + let mut buf = [0 as c_char; TMPBUF_SZ]; + + let p = buf.as_mut_ptr(); + unsafe { + if strerror_r(errno as c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + + let p = p as *const _; + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + } +} + +#[cfg(target_os = "espidf")] +pub fn getcwd() -> io::Result<PathBuf> { + Ok(PathBuf::from("/")) +} + +#[cfg(not(target_os = "espidf"))] +pub fn getcwd() -> io::Result<PathBuf> { + let mut buf = Vec::with_capacity(512); + loop { + unsafe { + let ptr = buf.as_mut_ptr() as *mut libc::c_char; + if !libc::getcwd(ptr, buf.capacity()).is_null() { + let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); + buf.set_len(len); + buf.shrink_to_fit(); + return Ok(PathBuf::from(OsString::from_vec(buf))); + } else { + let error = io::Error::last_os_error(); + if error.raw_os_error() != Some(libc::ERANGE) { + return Err(error); + } + } + + // Trigger the internal buffer resizing logic of `Vec` by requiring + // more space than the current capacity. + let cap = buf.capacity(); + buf.set_len(cap); + buf.reserve(1); + } + } +} + +#[cfg(target_os = "espidf")] +pub fn chdir(p: &path::Path) -> io::Result<()> { + super::unsupported::unsupported() +} + +#[cfg(not(target_os = "espidf"))] +pub fn chdir(p: &path::Path) -> io::Result<()> { + let p: &OsStr = p.as_ref(); + let p = CString::new(p.as_bytes())?; + if unsafe { libc::chdir(p.as_ptr()) } != 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) +} + +pub struct SplitPaths<'a> { + iter: iter::Map<slice::Split<'a, u8, fn(&u8) -> bool>, fn(&'a [u8]) -> PathBuf>, +} + +pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { + fn bytes_to_path(b: &[u8]) -> PathBuf { + PathBuf::from(<OsStr as OsStrExt>::from_bytes(b)) + } + fn is_separator(b: &u8) -> bool { + *b == PATH_SEPARATOR + } + let unparsed = unparsed.as_bytes(); + SplitPaths { + iter: unparsed + .split(is_separator as fn(&u8) -> bool) + .map(bytes_to_path as fn(&[u8]) -> PathBuf), + } +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option<PathBuf> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths<I, T>(paths: I) -> Result<OsString, JoinPathsError> +where + I: Iterator<Item = T>, + T: AsRef<OsStr>, +{ + let mut joined = Vec::new(); + + for (i, path) in paths.enumerate() { + let path = path.as_ref().as_bytes(); + if i > 0 { + joined.push(PATH_SEPARATOR) + } + if path.contains(&PATH_SEPARATOR) { + return Err(JoinPathsError); + } + joined.extend_from_slice(path); + } + Ok(OsStringExt::from_vec(joined)) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "path segment contains separator `{}`", char::from(PATH_SEPARATOR)) + } +} + +impl StdError for JoinPathsError { + #[allow(deprecated)] + fn description(&self) -> &str { + "failed to join paths" + } +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +pub fn current_exe() -> io::Result<PathBuf> { + unsafe { + let mut mib = [ + libc::CTL_KERN as c_int, + libc::KERN_PROC as c_int, + libc::KERN_PROC_PATHNAME as c_int, + -1 as c_int, + ]; + let mut sz = 0; + cvt(libc::sysctl( + mib.as_mut_ptr(), + mib.len() as libc::c_uint, + ptr::null_mut(), + &mut sz, + ptr::null_mut(), + 0, + ))?; + if sz == 0 { + return Err(io::Error::last_os_error()); + } + let mut v: Vec<u8> = Vec::with_capacity(sz); + cvt(libc::sysctl( + mib.as_mut_ptr(), + mib.len() as libc::c_uint, + v.as_mut_ptr() as *mut libc::c_void, + &mut sz, + ptr::null_mut(), + 0, + ))?; + if sz == 0 { + return Err(io::Error::last_os_error()); + } + v.set_len(sz - 1); // chop off trailing NUL + Ok(PathBuf::from(OsString::from_vec(v))) + } +} + +#[cfg(target_os = "netbsd")] +pub fn current_exe() -> io::Result<PathBuf> { + fn sysctl() -> io::Result<PathBuf> { + unsafe { + let mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, -1, libc::KERN_PROC_PATHNAME]; + let mut path_len: usize = 0; + cvt(libc::sysctl( + mib.as_ptr(), + mib.len() as libc::c_uint, + ptr::null_mut(), + &mut path_len, + ptr::null(), + 0, + ))?; + if path_len <= 1 { + return Err(io::const_io_error!( + io::ErrorKind::Uncategorized, + "KERN_PROC_PATHNAME sysctl returned zero-length string", + )); + } + let mut path: Vec<u8> = Vec::with_capacity(path_len); + cvt(libc::sysctl( + mib.as_ptr(), + mib.len() as libc::c_uint, + path.as_ptr() as *mut libc::c_void, + &mut path_len, + ptr::null(), + 0, + ))?; + path.set_len(path_len - 1); // chop off NUL + Ok(PathBuf::from(OsString::from_vec(path))) + } + } + fn procfs() -> io::Result<PathBuf> { + let curproc_exe = path::Path::new("/proc/curproc/exe"); + if curproc_exe.is_file() { + return crate::fs::read_link(curproc_exe); + } + Err(io::const_io_error!( + io::ErrorKind::Uncategorized, + "/proc/curproc/exe doesn't point to regular file.", + )) + } + sysctl().or_else(|_| procfs()) +} + +#[cfg(target_os = "openbsd")] +pub fn current_exe() -> io::Result<PathBuf> { + unsafe { + let mut mib = [libc::CTL_KERN, libc::KERN_PROC_ARGS, libc::getpid(), libc::KERN_PROC_ARGV]; + let mib = mib.as_mut_ptr(); + let mut argv_len = 0; + cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len, ptr::null_mut(), 0))?; + let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize); + cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?; + argv.set_len(argv_len as usize); + if argv[0].is_null() { + return Err(io::const_io_error!( + io::ErrorKind::Uncategorized, + "no current exe available", + )); + } + let argv0 = CStr::from_ptr(argv[0]).to_bytes(); + if argv0[0] == b'.' || argv0.iter().any(|b| *b == b'/') { + crate::fs::canonicalize(OsStr::from_bytes(argv0)) + } else { + Ok(PathBuf::from(OsStr::from_bytes(argv0))) + } + } +} + +#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] +pub fn current_exe() -> io::Result<PathBuf> { + match crate::fs::read_link("/proc/self/exe") { + Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_io_error!( + io::ErrorKind::Uncategorized, + "no /proc/self/exe available. Is /proc mounted?", + )), + other => other, + } +} + +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] +pub fn current_exe() -> io::Result<PathBuf> { + unsafe { + let mut sz: u32 = 0; + libc::_NSGetExecutablePath(ptr::null_mut(), &mut sz); + if sz == 0 { + return Err(io::Error::last_os_error()); + } + let mut v: Vec<u8> = Vec::with_capacity(sz as usize); + let err = libc::_NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); + if err != 0 { + return Err(io::Error::last_os_error()); + } + v.set_len(sz as usize - 1); // chop off trailing NUL + Ok(PathBuf::from(OsString::from_vec(v))) + } +} + +#[cfg(any(target_os = "solaris", target_os = "illumos"))] +pub fn current_exe() -> io::Result<PathBuf> { + if let Ok(path) = crate::fs::read_link("/proc/self/path/a.out") { + Ok(path) + } else { + unsafe { + let path = libc::getexecname(); + if path.is_null() { + Err(io::Error::last_os_error()) + } else { + let filename = CStr::from_ptr(path).to_bytes(); + let path = PathBuf::from(<OsStr as OsStrExt>::from_bytes(filename)); + + // Prepend a current working directory to the path if + // it doesn't contain an absolute pathname. + if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) } + } + } + } +} + +#[cfg(target_os = "haiku")] +pub fn current_exe() -> io::Result<PathBuf> { + unsafe { + let mut info: mem::MaybeUninit<libc::image_info> = mem::MaybeUninit::uninit(); + let mut cookie: i32 = 0; + // the executable can be found at team id 0 + let result = libc::_get_next_image_info( + 0, + &mut cookie, + info.as_mut_ptr(), + mem::size_of::<libc::image_info>(), + ); + if result != 0 { + use crate::io::ErrorKind; + Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path")) + } else { + let name = CStr::from_ptr((*info.as_ptr()).name.as_ptr()).to_bytes(); + Ok(PathBuf::from(OsStr::from_bytes(name))) + } + } +} + +#[cfg(target_os = "redox")] +pub fn current_exe() -> io::Result<PathBuf> { + crate::fs::read_to_string("sys:exe").map(PathBuf::from) +} + +#[cfg(target_os = "l4re")] +pub fn current_exe() -> io::Result<PathBuf> { + use crate::io::ErrorKind; + Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!")) +} + +#[cfg(target_os = "vxworks")] +pub fn current_exe() -> io::Result<PathBuf> { + #[cfg(test)] + use realstd::env; + + #[cfg(not(test))] + use crate::env; + + let exe_path = env::args().next().unwrap(); + let path = path::Path::new(&exe_path); + path.canonicalize() +} + +#[cfg(any(target_os = "espidf", target_os = "horizon"))] +pub fn current_exe() -> io::Result<PathBuf> { + super::unsupported::unsupported() +} + +#[cfg(target_os = "fuchsia")] +pub fn current_exe() -> io::Result<PathBuf> { + use crate::io::ErrorKind; + + #[cfg(test)] + use realstd::env; + + #[cfg(not(test))] + use crate::env; + + let exe_path = env::args().next().ok_or(io::const_io_error!( + ErrorKind::Uncategorized, + "an executable path was not found because no arguments were provided through argv" + ))?; + let path = PathBuf::from(exe_path); + + // Prepend the current working directory to the path if it's not absolute. + if !path.is_absolute() { getcwd().map(|cwd| cwd.join(path)) } else { Ok(path) } +} + +pub struct Env { + iter: vec::IntoIter<(OsString, OsString)>, +} + +impl !Send for Env {} +impl !Sync for Env {} + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.iter.size_hint() + } +} + +#[cfg(target_os = "macos")] +pub unsafe fn environ() -> *mut *const *const c_char { + libc::_NSGetEnviron() as *mut *const *const c_char +} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn environ() -> *mut *const *const c_char { + extern "C" { + static mut environ: *const *const c_char; + } + ptr::addr_of_mut!(environ) +} + +static ENV_LOCK: StaticRwLock = StaticRwLock::new(); + +pub fn env_read_lock() -> StaticRwLockReadGuard { + ENV_LOCK.read() +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + unsafe { + let _guard = env_read_lock(); + let mut environ = *environ(); + let mut result = Vec::new(); + if !environ.is_null() { + while !(*environ).is_null() { + if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { + result.push(key_value); + } + environ = environ.add(1); + } + } + return Env { iter: result.into_iter() }; + } + + fn parse(input: &[u8]) -> Option<(OsString, OsString)> { + // Strategy (copied from glibc): Variable name and value are separated + // by an ASCII equals sign '='. Since a variable name must not be + // empty, allow variable names starting with an equals sign. Skip all + // malformed lines. + if input.is_empty() { + return None; + } + let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); + pos.map(|p| { + ( + OsStringExt::from_vec(input[..p].to_vec()), + OsStringExt::from_vec(input[p + 1..].to_vec()), + ) + }) + } +} + +pub fn getenv(k: &OsStr) -> Option<OsString> { + // environment variables with a nul byte can't be set, so their value is + // always None as well + let k = CString::new(k.as_bytes()).ok()?; + unsafe { + let _guard = env_read_lock(); + let s = libc::getenv(k.as_ptr()) as *const libc::c_char; + if s.is_null() { + None + } else { + Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) + } + } +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let k = CString::new(k.as_bytes())?; + let v = CString::new(v.as_bytes())?; + + unsafe { + let _guard = ENV_LOCK.write(); + cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) + } +} + +pub fn unsetenv(n: &OsStr) -> io::Result<()> { + let nbuf = CString::new(n.as_bytes())?; + + unsafe { + let _guard = ENV_LOCK.write(); + cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) + } +} + +#[cfg(not(target_os = "espidf"))] +pub fn page_size() -> usize { + unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } +} + +pub fn temp_dir() -> PathBuf { + crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| { + if cfg!(target_os = "android") { + PathBuf::from("/data/local/tmp") + } else { + PathBuf::from("/tmp") + } + }) +} + +pub fn home_dir() -> Option<PathBuf> { + return crate::env::var_os("HOME").or_else(|| unsafe { fallback() }).map(PathBuf::from); + + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "watchos", + target_os = "emscripten", + target_os = "redox", + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon" + ))] + unsafe fn fallback() -> Option<OsString> { + None + } + #[cfg(not(any( + target_os = "android", + target_os = "ios", + target_os = "watchos", + target_os = "emscripten", + target_os = "redox", + target_os = "vxworks", + target_os = "espidf", + target_os = "horizon" + )))] + unsafe fn fallback() -> Option<OsString> { + let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { + n if n < 0 => 512 as usize, + n => n as usize, + }; + let mut buf = Vec::with_capacity(amt); + let mut passwd: libc::passwd = mem::zeroed(); + let mut result = ptr::null_mut(); + match libc::getpwuid_r( + libc::getuid(), + &mut passwd, + buf.as_mut_ptr(), + buf.capacity(), + &mut result, + ) { + 0 if !result.is_null() => { + let ptr = passwd.pw_dir as *const _; + let bytes = CStr::from_ptr(ptr).to_bytes().to_vec(); + Some(OsStringExt::from_vec(bytes)) + } + _ => None, + } + } +} + +pub fn exit(code: i32) -> ! { + unsafe { libc::exit(code as c_int) } +} + +pub fn getpid() -> u32 { + unsafe { libc::getpid() as u32 } +} + +pub fn getppid() -> u32 { + unsafe { libc::getppid() as u32 } +} + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +pub fn glibc_version() -> Option<(usize, usize)> { + extern "C" { + fn gnu_get_libc_version() -> *const libc::c_char; + } + let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) }; + if let Ok(version_str) = version_cstr.to_str() { + parse_glibc_version(version_str) + } else { + None + } +} + +// Returns Some((major, minor)) if the string is a valid "x.y" version, +// ignoring any extra dot-separated parts. Otherwise return None. +#[cfg(all(target_os = "linux", target_env = "gnu"))] +fn parse_glibc_version(version: &str) -> Option<(usize, usize)> { + let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse(); + match (parsed_ints.next(), parsed_ints.next()) { + (Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)), + _ => None, + } +} diff --git a/library/std/src/sys/unix/os/tests.rs b/library/std/src/sys/unix/os/tests.rs new file mode 100644 index 000000000..efc29955b --- /dev/null +++ b/library/std/src/sys/unix/os/tests.rs @@ -0,0 +1,23 @@ +#[test] +#[cfg(all(target_os = "linux", target_env = "gnu"))] +fn test_glibc_version() { + // This mostly just tests that the weak linkage doesn't panic wildly... + super::glibc_version(); +} + +#[test] +#[cfg(all(target_os = "linux", target_env = "gnu"))] +fn test_parse_glibc_version() { + let cases = [ + ("0.0", Some((0, 0))), + ("01.+2", Some((1, 2))), + ("3.4.5.six", Some((3, 4))), + ("1", None), + ("1.-2", None), + ("1.foo", None), + ("foo.1", None), + ]; + for &(version_str, parsed) in cases.iter() { + assert_eq!(parsed, super::parse_glibc_version(version_str)); + } +} diff --git a/library/std/src/sys/unix/os_str.rs b/library/std/src/sys/unix/os_str.rs new file mode 100644 index 000000000..ccbc18224 --- /dev/null +++ b/library/std/src/sys/unix/os_str.rs @@ -0,0 +1,266 @@ +//! The underlying OsString/OsStr implementation on Unix and many other +//! systems: just a `Vec<u8>`/`[u8]`. + +use crate::borrow::Cow; +use crate::collections::TryReserveError; +use crate::fmt; +use crate::fmt::Write; +use crate::mem; +use crate::rc::Rc; +use crate::str; +use crate::sync::Arc; +use crate::sys_common::{AsInner, IntoInner}; + +use core::str::lossy::{Utf8Lossy, Utf8LossyChunk}; + +#[cfg(test)] +#[path = "../unix/os_str/tests.rs"] +mod tests; + +#[derive(Hash)] +#[repr(transparent)] +pub struct Buf { + pub inner: Vec<u8>, +} + +#[repr(transparent)] +pub struct Slice { + pub inner: [u8], +} + +impl fmt::Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + // Writes out a valid unicode string with the correct escape sequences + + formatter.write_str("\"")?; + for Utf8LossyChunk { valid, broken } in Utf8Lossy::from_bytes(&self.inner).chunks() { + for c in valid.chars().flat_map(|c| c.escape_debug()) { + formatter.write_char(c)? + } + + for b in broken { + write!(formatter, "\\x{:02X}", b)?; + } + } + formatter.write_str("\"") + } +} + +impl fmt::Display for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), formatter) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), formatter) + } +} + +impl Clone for Buf { + #[inline] + fn clone(&self) -> Self { + Buf { inner: self.inner.clone() } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + self.inner.clone_from(&source.inner) + } +} + +impl IntoInner<Vec<u8>> for Buf { + fn into_inner(self) -> Vec<u8> { + self.inner + } +} + +impl AsInner<[u8]> for Buf { + fn as_inner(&self) -> &[u8] { + &self.inner + } +} + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Buf { + Buf { inner: Vec::with_capacity(capacity) } + } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + #[inline] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + #[inline] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { + self.inner.try_reserve_exact(additional) + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + + #[inline] + pub fn as_slice(&self) -> &Slice { + // SAFETY: Slice just wraps [u8], + // and &*self.inner is &[u8], therefore + // transmuting &[u8] to &Slice is safe. + unsafe { mem::transmute(&*self.inner) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut Slice { + // SAFETY: Slice just wraps [u8], + // and &mut *self.inner is &mut [u8], therefore + // transmuting &mut [u8] to &mut Slice is safe. + unsafe { mem::transmute(&mut *self.inner) } + } + + pub fn into_string(self) -> Result<String, Buf> { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() }) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.extend_from_slice(&s.inner) + } + + #[inline] + pub fn into_box(self) -> Box<Slice> { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } + + #[inline] + pub fn from_box(boxed: Box<Slice>) -> Buf { + let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_vec() } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + self.as_slice().into_rc() + } +} + +impl Slice { + #[inline] + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + #[inline] + pub fn from_str(s: &str) -> &Slice { + Slice::from_u8_slice(s.as_bytes()) + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> Cow<'_, str> { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } + + pub fn clone_into(&self, buf: &mut Buf) { + self.inner.clone_into(&mut buf.inner) + } + + #[inline] + pub fn into_box(&self) -> Box<Slice> { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box<Slice> { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + let arc: Arc<[u8]> = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + let rc: Rc<[u8]> = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } + + #[inline] + pub fn make_ascii_lowercase(&mut self) { + self.inner.make_ascii_lowercase() + } + + #[inline] + pub fn make_ascii_uppercase(&mut self) { + self.inner.make_ascii_uppercase() + } + + #[inline] + pub fn to_ascii_lowercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_lowercase() } + } + + #[inline] + pub fn to_ascii_uppercase(&self) -> Buf { + Buf { inner: self.inner.to_ascii_uppercase() } + } + + #[inline] + pub fn is_ascii(&self) -> bool { + self.inner.is_ascii() + } + + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { + self.inner.eq_ignore_ascii_case(&other.inner) + } +} diff --git a/library/std/src/sys/unix/os_str/tests.rs b/library/std/src/sys/unix/os_str/tests.rs new file mode 100644 index 000000000..213277f01 --- /dev/null +++ b/library/std/src/sys/unix/os_str/tests.rs @@ -0,0 +1,10 @@ +use super::*; + +#[test] +fn slice_debug_output() { + let input = Slice::from_u8_slice(b"\xF0hello,\tworld"); + let expected = r#""\xF0hello,\tworld""#; + let output = format!("{input:?}"); + + assert_eq!(output, expected); +} |