diff --git a/src/lib.rs b/src/lib.rs index f1db68b..1595eb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(rust_2018_idioms)] #![cfg_attr(feature = "nightly", feature(const_raw_ptr_deref))] +#![cfg_attr(feature = "nightly", feature(const_slice_from_raw_parts))] +#![cfg_attr(feature = "nightly", feature(const_str_from_utf8))] #[cfg(test)] extern crate std; @@ -1155,11 +1157,40 @@ impl CStr { /// assert_eq!(cstr.to_bytes(), b"foo"); /// ``` #[inline] + #[cfg(not(feature = "nightly"))] pub fn to_bytes(&self) -> &[u8] { let bytes = self.to_bytes_with_nul(); &bytes[..bytes.len() - 1] } + /// Converts this C string to a byte slice. + /// + /// The returned slice will **not** contain the trailing nul terminator that this C + /// string has. + /// + /// > **Note**: This method is currently implemented as a constant-time + /// > cast, but it is planned to alter its definition in the future to + /// > perform the length calculation whenever this method is called. + /// + /// # Examples + /// + /// ``` + /// use cstr_core::CStr; + /// + /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); + /// assert_eq!(cstr.to_bytes(), b"foo"); + /// ``` + #[inline] + #[cfg(feature = "nightly")] + pub const fn to_bytes(&self) -> &[u8] { + let bytes = self.to_bytes_with_nul(); + // unsafe: This is just like [:len - 1] (but const usable), and any underflow of the `- 1` + // is avoided by the type's guarantee that there is a trailing nul byte. + unsafe { + slice::from_raw_parts(bytes.as_ptr(), bytes.len() - 1) + } + } + /// Converts this C string to a byte slice containing the trailing 0 byte. /// /// This function is the equivalent of [`to_bytes`] except that it will retain @@ -1180,6 +1211,32 @@ impl CStr { /// assert_eq!(cstr.to_bytes_with_nul(), b"foo\0"); /// ``` #[inline] + #[cfg(feature = "nightly")] + pub const fn to_bytes_with_nul(&self) -> &[u8] { + unsafe { &*(&self.inner as *const [c_char] as *const [u8]) } + } + + /// Converts this C string to a byte slice containing the trailing 0 byte. + /// + /// This function is the equivalent of [`to_bytes`] except that it will retain + /// the trailing nul terminator instead of chopping it off. + /// + /// > **Note**: This method is currently implemented as a 0-cost cast, but + /// > it is planned to alter its definition in the future to perform the + /// > length calculation whenever this method is called. + /// + /// [`to_bytes`]: #method.to_bytes + /// + /// # Examples + /// + /// ``` + /// use cstr_core::CStr; + /// + /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); + /// assert_eq!(cstr.to_bytes_with_nul(), b"foo\0"); + /// ``` + #[inline] + #[cfg(not(feature = "nightly"))] pub fn to_bytes_with_nul(&self) -> &[u8] { unsafe { &*(&self.inner as *const [c_char] as *const [u8]) } } @@ -1198,7 +1255,31 @@ impl CStr { /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); /// assert_eq!(cstr.to_str(), Ok("foo")); /// ``` - pub fn to_str(&self) -> Result<&str, Utf8Error> { + #[cfg(not(feature = "nightly"))] + pub fn to_str(&self) -> Result<&str, Utf8Error> { + // N.B., when `CStr` is changed to perform the length check in `.to_bytes()` + // instead of in `from_ptr()`, it may be worth considering if this should + // be rewritten to do the UTF-8 check inline with the length calculation + // instead of doing it afterwards. + str::from_utf8(self.to_bytes()) + } + + /// Yields a [`&str`] slice if the `CStr` contains valid UTF-8. + /// + /// If the contents of the `CStr` are valid UTF-8 data, this + /// function will return the corresponding [`&str`] slice. Otherwise, + /// it will return an error with details of where UTF-8 validation failed. + /// + /// # Examples + /// + /// ``` + /// use cstr_core::CStr; + /// + /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); + /// assert_eq!(cstr.to_str(), Ok("foo")); + /// ``` + #[cfg(feature = "nightly")] + pub const fn to_str(&self) -> Result<&str, Utf8Error> { // N.B., when `CStr` is changed to perform the length check in `.to_bytes()` // instead of in `from_ptr()`, it may be worth considering if this should // be rewritten to do the UTF-8 check inline with the length calculation