diff options
| author | Ed Page <eopage@gmail.com> | 2023-03-27 21:22:36 -0500 |
|---|---|---|
| committer | Ed Page <eopage@gmail.com> | 2023-03-27 22:29:44 -0500 |
| commit | 8d2beb50c2b228f4bd6f8e2d81d82c9e1f5e5ba2 (patch) | |
| tree | de100128b6cc7933b991384b581f9cef3135724d /library/std/src/ffi | |
| parent | 70e04bd88d85cab8ed110ace5a278fab106d0ef5 (diff) | |
Allow access to `OsStr` bytes
`OsStr` has historically kept its implementation details private out of concern for locking us into a specific encoding on Windows. This is an alternative to #95290 which proposed specifying the encoding on Windows. Instead, this only specifies that for cross-platform code, `OsStr`'s encoding is a superset of UTF-8 and defines rules for safely interacting with it At minimum, this can greatly simplify the `os_str_bytes` crate and every arg parser that interacts with `OsStr` directly (which is most of those that support invalid UTF-8).
Diffstat (limited to 'library/std/src/ffi')
| -rw-r--r-- | library/std/src/ffi/mod.rs | 8 | ||||
| -rw-r--r-- | library/std/src/ffi/os_str.rs | 56 |
2 files changed, 63 insertions, 1 deletions
diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index d987bf69b25..3ddb8748753 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -127,6 +127,14 @@ //! trait, which provides a [`from_wide`] method to convert a native Windows //! string (without the terminating nul character) to an [`OsString`]. //! +//! ## On all platforms +//! +//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of +//! UTF-8; see [`OsString`] for more details on its encoding on different platforms. +//! +//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_os_str_bytes`] and +//! [`OsStr::from_os_str_bytes_unchecked`]. +//! //! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value //! [Unicode code point]: https://www.unicode.org/glossary/#code_point //! [`env::set_var()`]: crate::env::set_var "env::set_var" diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 5c0541d3caf..25ab2196688 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -667,6 +667,45 @@ impl OsStr { s.as_ref() } + /// Converts a slice of bytes to an OS string slice without checking that the string contains + /// valid `OsStr`-encoded data. + /// + /// See the [module's toplevel documentation about conversions][conversions] for safe, + /// cross-platform [conversions] from/to native representations. + /// + /// # Safety + /// + /// `OsStr`'s encoding is an unspecified superset of UTF-8 and callers must + /// pass in bytes that originated as a mixture of validated UTF-8 and bytes from + /// [`OsStr::as_os_str_bytes`] from within the same rust version built for the same target + /// platform. The bytes from `OsStr::as_os_str_bytes` may be split either + /// immediately before or immediately after some valid non-empty UTF-8 substring + /// + /// # Example + /// + /// ``` + /// #![feature(os_str_bytes)] + /// + /// use std::ffi::OsStr; + /// + /// let os_str = OsStr::new("Mary had a little lamb"); + /// let bytes = os_str.as_os_str_bytes(); + /// let words = bytes.split(|b| *b == b' '); + /// let words: Vec<&OsStr> = words.map(|word| { + /// // SAFETY: + /// // - Each `word` only contains content that originated from `OsStr::as_os_str_bytes` + /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring + /// unsafe { OsStr::from_os_str_bytes_unchecked(word) } + /// }).collect(); + /// ``` + /// + /// [conversions]: super#conversions + #[inline] + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub fn from_os_str_bytes_unchecked(bytes: &[u8]) -> &Self { + Self::from_inner(Slice::from_os_str_bytes_unchecked(bytes)) + } + #[inline] fn from_inner(inner: &Slice) -> &OsStr { // SAFETY: OsStr is just a wrapper of Slice, @@ -837,13 +876,28 @@ impl OsStr { OsString { inner: Buf::from_box(boxed) } } + /// Converts an OS string slice to a byte slice. To convert the byte slice back into an OS + /// string slice, use the [`OsStr::from_os_str_bytes_unchecked`] function. + /// + /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should + /// be treated as opaque and only comparable within the same rust version built for the same + /// target platform. See [`OsString`] for more encoding details and [`std::ffi`] for + /// platform-specific, specified conversions. + /// + /// [`std::ffi`]: crate::ffi + #[inline] + #[unstable(feature = "os_str_bytes", issue = "111544")] + pub fn as_os_str_bytes(&self) -> &[u8] { + self.inner.as_os_str_bytes() + } + /// Gets the underlying byte representation. /// /// Note: it is *crucial* that this API is not externally public, to avoid /// revealing the internal, platform-specific encodings. #[inline] pub(crate) fn bytes(&self) -> &[u8] { - unsafe { &*(&self.inner as *const _ as *const [u8]) } + self.as_os_str_bytes() } /// Converts this string to its ASCII lower case equivalent in-place. |
