@@ -234,15 +234,14 @@ pub struct NulError(usize, Vec<u8>);
234234
235235/// An error indicating that a nul byte was not in the expected position.
236236///
237- /// The slice used to create a [`CStr`] must have one and only one nul
238- /// byte at the end of the slice .
237+ /// The slice used to create a [`CStr`] must have one and only one nul byte,
238+ /// positioned at the end.
239239///
240- /// This error is created by the
241- /// [`from_bytes_with_nul`][`CStr::from_bytes_with_nul`] method on
242- /// [`CStr`]. See its documentation for more.
240+ /// This error is created by the [`from_bytes_with_nul`] method on [`CStr`].
241+ /// See its documentation for more.
243242///
244243/// [`CStr`]: struct.CStr.html
245- /// [`CStr:: from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul
244+ /// [`from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul
246245///
247246/// # Examples
248247///
@@ -257,6 +256,32 @@ pub struct FromBytesWithNulError {
257256 kind : FromBytesWithNulErrorKind ,
258257}
259258
259+ /// An error indicating that a nul byte was not in the expected position.
260+ ///
261+ /// The vector used to create a [`CString`] must have one and only one nul byte,
262+ /// positioned at the end.
263+ ///
264+ /// This error is created by the [`from_vec_with_nul`] method on [`CString`].
265+ /// See its documentation for more.
266+ ///
267+ /// [`CString`]: struct.CString.html
268+ /// [`from_vec_with_nul`]: struct.CString.html#method.from_vec_with_nul
269+ ///
270+ /// # Examples
271+ ///
272+ /// ```
273+ /// #![feature(cstring_from_vec_with_nul)]
274+ /// use std::ffi::{CString, FromVecWithNulError};
275+ ///
276+ /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"f\0oo".to_vec()).unwrap_err();
277+ /// ```
278+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
279+ #[ unstable( feature = "cstring_from_vec_with_nul" , issue = "73179" ) ]
280+ pub struct FromVecWithNulError {
281+ error_kind : FromBytesWithNulErrorKind ,
282+ bytes : Vec < u8 > ,
283+ }
284+
260285#[ derive( Clone , PartialEq , Eq , Debug ) ]
261286enum FromBytesWithNulErrorKind {
262287 InteriorNul ( usize ) ,
@@ -272,6 +297,59 @@ impl FromBytesWithNulError {
272297 }
273298}
274299
300+ #[ unstable( feature = "cstring_from_vec_with_nul" , issue = "73179" ) ]
301+ impl FromVecWithNulError {
302+ /// Returns a slice of [`u8`]s bytes that were attempted to convert to a [`CString`].
303+ ///
304+ /// # Examples
305+ ///
306+ /// Basic usage:
307+ ///
308+ /// ```
309+ /// #![feature(cstring_from_vec_with_nul)]
310+ /// use std::ffi::CString;
311+ ///
312+ /// // Some invalid bytes in a vector
313+ /// let bytes = b"f\0oo".to_vec();
314+ ///
315+ /// let value = CString::from_vec_with_nul(bytes.clone());
316+ ///
317+ /// assert_eq!(&bytes[..], value.unwrap_err().as_bytes());
318+ /// ```
319+ ///
320+ /// [`CString`]: struct.CString.html
321+ pub fn as_bytes ( & self ) -> & [ u8 ] {
322+ & self . bytes [ ..]
323+ }
324+
325+ /// Returns the bytes that were attempted to convert to a [`CString`].
326+ ///
327+ /// This method is carefully constructed to avoid allocation. It will
328+ /// consume the error, moving out the bytes, so that a copy of the bytes
329+ /// does not need to be made.
330+ ///
331+ /// # Examples
332+ ///
333+ /// Basic usage:
334+ ///
335+ /// ```
336+ /// #![feature(cstring_from_vec_with_nul)]
337+ /// use std::ffi::CString;
338+ ///
339+ /// // Some invalid bytes in a vector
340+ /// let bytes = b"f\0oo".to_vec();
341+ ///
342+ /// let value = CString::from_vec_with_nul(bytes.clone());
343+ ///
344+ /// assert_eq!(bytes, value.unwrap_err().into_bytes());
345+ /// ```
346+ ///
347+ /// [`CString`]: struct.CString.html
348+ pub fn into_bytes ( self ) -> Vec < u8 > {
349+ self . bytes
350+ }
351+ }
352+
275353/// An error indicating invalid UTF-8 when converting a [`CString`] into a [`String`].
276354///
277355/// `CString` is just a wrapper over a buffer of bytes with a nul
@@ -643,6 +721,86 @@ impl CString {
643721 let this = mem:: ManuallyDrop :: new ( self ) ;
644722 unsafe { ptr:: read ( & this. inner ) }
645723 }
724+
725+ /// Converts a `Vec` of `u8` to a `CString` without checking the invariants
726+ /// on the given `Vec`.
727+ ///
728+ /// # Safety
729+ ///
730+ /// The given `Vec` **must** have one nul byte as its last element.
731+ /// This means it cannot be empty nor have any other nul byte anywhere else.
732+ ///
733+ /// # Example
734+ ///
735+ /// ```
736+ /// #![feature(cstring_from_vec_with_nul)]
737+ /// use std::ffi::CString;
738+ /// assert_eq!(
739+ /// unsafe { CString::from_vec_with_nul_unchecked(b"abc\0".to_vec()) },
740+ /// unsafe { CString::from_vec_unchecked(b"abc".to_vec()) }
741+ /// );
742+ /// ```
743+ #[ unstable( feature = "cstring_from_vec_with_nul" , issue = "73179" ) ]
744+ pub unsafe fn from_vec_with_nul_unchecked ( v : Vec < u8 > ) -> Self {
745+ Self { inner : v. into_boxed_slice ( ) }
746+ }
747+
748+ /// Attempts to converts a `Vec` of `u8` to a `CString`.
749+ ///
750+ /// Runtime checks are present to ensure there is only one nul byte in the
751+ /// `Vec`, its last element.
752+ ///
753+ /// # Errors
754+ ///
755+ /// If a nul byte is present and not the last element or no nul bytes
756+ /// is present, an error will be returned.
757+ ///
758+ /// # Examples
759+ ///
760+ /// A successful conversion will produce the same result as [`new`] when
761+ /// called without the ending nul byte.
762+ ///
763+ /// ```
764+ /// #![feature(cstring_from_vec_with_nul)]
765+ /// use std::ffi::CString;
766+ /// assert_eq!(
767+ /// CString::from_vec_with_nul(b"abc\0".to_vec())
768+ /// .expect("CString::from_vec_with_nul failed"),
769+ /// CString::new(b"abc".to_vec()).expect("CString::new failed")
770+ /// );
771+ /// ```
772+ ///
773+ /// A incorrectly formatted vector will produce an error.
774+ ///
775+ /// ```
776+ /// #![feature(cstring_from_vec_with_nul)]
777+ /// use std::ffi::{CString, FromVecWithNulError};
778+ /// // Interior nul byte
779+ /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"a\0bc".to_vec()).unwrap_err();
780+ /// // No nul byte
781+ /// let _: FromVecWithNulError = CString::from_vec_with_nul(b"abc".to_vec()).unwrap_err();
782+ /// ```
783+ ///
784+ /// [`new`]: #method.new
785+ #[ unstable( feature = "cstring_from_vec_with_nul" , issue = "73179" ) ]
786+ pub fn from_vec_with_nul ( v : Vec < u8 > ) -> Result < Self , FromVecWithNulError > {
787+ let nul_pos = memchr:: memchr ( 0 , & v) ;
788+ match nul_pos {
789+ Some ( nul_pos) if nul_pos + 1 == v. len ( ) => {
790+ // SAFETY: We know there is only one nul byte, at the end
791+ // of the vec.
792+ Ok ( unsafe { Self :: from_vec_with_nul_unchecked ( v) } )
793+ }
794+ Some ( nul_pos) => Err ( FromVecWithNulError {
795+ error_kind : FromBytesWithNulErrorKind :: InteriorNul ( nul_pos) ,
796+ bytes : v,
797+ } ) ,
798+ None => Err ( FromVecWithNulError {
799+ error_kind : FromBytesWithNulErrorKind :: NotNulTerminated ,
800+ bytes : v,
801+ } ) ,
802+ }
803+ }
646804}
647805
648806// Turns this `CString` into an empty string to prevent
@@ -976,6 +1134,23 @@ impl fmt::Display for FromBytesWithNulError {
9761134 }
9771135}
9781136
1137+ #[ unstable( feature = "cstring_from_vec_with_nul" , issue = "73179" ) ]
1138+ impl Error for FromVecWithNulError { }
1139+
1140+ #[ unstable( feature = "cstring_from_vec_with_nul" , issue = "73179" ) ]
1141+ impl fmt:: Display for FromVecWithNulError {
1142+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1143+ match self . error_kind {
1144+ FromBytesWithNulErrorKind :: InteriorNul ( pos) => {
1145+ write ! ( f, "data provided contains an interior nul byte at pos {}" , pos)
1146+ }
1147+ FromBytesWithNulErrorKind :: NotNulTerminated => {
1148+ write ! ( f, "data provided is not nul terminated" )
1149+ }
1150+ }
1151+ }
1152+ }
1153+
9791154impl IntoStringError {
9801155 /// Consumes this error, returning original [`CString`] which generated the
9811156 /// error.
0 commit comments