Skip to content

Commit 9b2833a

Browse files
authored
Rollup merge of rust-lang#73139 - poliorcetics:cstring-from-vec-with-nul, r=dtolnay
Add methods to go from a nul-terminated Vec<u8> to a CString Fixes rust-lang#73100. Doc tests have been written and the documentation on the error type updated too. I used `#[stable(feature = "cstring_from_vec_with_nul", since = "1.46.0")]` but I don't know if the version is correct.
2 parents 13d5c27 + 47cc5cc commit 9b2833a

File tree

2 files changed

+183
-6
lines changed

2 files changed

+183
-6
lines changed

src/libstd/ffi/c_str.rs

+181-6
Original file line numberDiff line numberDiff line change
@@ -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)]
261286
enum 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+
9791154
impl IntoStringError {
9801155
/// Consumes this error, returning original [`CString`] which generated the
9811156
/// error.

src/libstd/ffi/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@
157157

158158
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
159159
pub use self::c_str::FromBytesWithNulError;
160+
#[unstable(feature = "cstring_from_vec_with_nul", issue = "73179")]
161+
pub use self::c_str::FromVecWithNulError;
160162
#[stable(feature = "rust1", since = "1.0.0")]
161163
pub use self::c_str::{CStr, CString, IntoStringError, NulError};
162164

0 commit comments

Comments
 (0)