Skip to content

Commit 281f9d8

Browse files
committed
Auto merge of #30614 - arcnmx:cstr-bytes, r=alexcrichton
I'm a bit iffy on the return type, but `Result` would also be a bit weird... The two error cases are `Unterminated` and `InteriorNul(usize)`. I considered `from_chars(&[c_char])` but this meshes better with `as_bytes_with_nul()` and Rust code in general. Should there also be a corresponding unsafe `from_bytes_unchecked` variant?
2 parents 43ddfbd + 71f29cd commit 281f9d8

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

src/libstd/ffi/c_str.rs

+78
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,57 @@ impl CStr {
436436
mem::transmute(slice::from_raw_parts(ptr, len as usize + 1))
437437
}
438438

439+
/// Creates a C string wrapper from a byte slice.
440+
///
441+
/// This function will cast the provided `bytes` to a `CStr` wrapper after
442+
/// ensuring that it is null terminated and does not contain any interior
443+
/// nul bytes.
444+
///
445+
/// # Examples
446+
///
447+
/// ```
448+
/// # #![feature(cstr_from_bytes)]
449+
/// use std::ffi::CStr;
450+
///
451+
/// # fn main() {
452+
/// let cstr = CStr::from_bytes_with_nul(b"hello\0");
453+
/// assert!(cstr.is_some());
454+
/// # }
455+
/// ```
456+
#[unstable(feature = "cstr_from_bytes", reason = "recently added", issue = "31190")]
457+
pub fn from_bytes_with_nul(bytes: &[u8]) -> Option<&CStr> {
458+
if bytes.is_empty() || memchr::memchr(0, &bytes) != Some(bytes.len() - 1) {
459+
None
460+
} else {
461+
Some(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
462+
}
463+
}
464+
465+
/// Unsafely creates a C string wrapper from a byte slice.
466+
///
467+
/// This function will cast the provided `bytes` to a `CStr` wrapper without
468+
/// performing any sanity checks. The provided slice must be null terminated
469+
/// and not contain any interior nul bytes.
470+
///
471+
/// # Examples
472+
///
473+
/// ```
474+
/// # #![feature(cstr_from_bytes)]
475+
/// use std::ffi::{CStr, CString};
476+
///
477+
/// # fn main() {
478+
/// unsafe {
479+
/// let cstring = CString::new("hello").unwrap();
480+
/// let cstr = CStr::from_bytes_with_nul_unchecked(cstring.to_bytes_with_nul());
481+
/// assert_eq!(cstr, &*cstring);
482+
/// }
483+
/// # }
484+
/// ```
485+
#[unstable(feature = "cstr_from_bytes", reason = "recently added", issue = "31190")]
486+
pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
487+
mem::transmute(bytes)
488+
}
489+
439490
/// Returns the inner pointer to this C string.
440491
///
441492
/// The returned pointer will be valid for as long as `self` is and points
@@ -670,4 +721,31 @@ mod tests {
670721

671722
assert_eq!(cstr_hash, cstring_hash);
672723
}
724+
725+
#[test]
726+
fn from_bytes_with_nul() {
727+
let data = b"123\0";
728+
let cstr = CStr::from_bytes_with_nul(data);
729+
assert_eq!(cstr.map(CStr::to_bytes), Some(&b"123"[..]));
730+
assert_eq!(cstr.map(CStr::to_bytes_with_nul), Some(&b"123\0"[..]));
731+
732+
unsafe {
733+
let cstr_unchecked = CStr::from_bytes_with_nul_unchecked(data);
734+
assert_eq!(cstr, Some(cstr_unchecked));
735+
}
736+
}
737+
738+
#[test]
739+
fn from_bytes_with_nul_unterminated() {
740+
let data = b"123";
741+
let cstr = CStr::from_bytes_with_nul(data);
742+
assert!(cstr.is_none());
743+
}
744+
745+
#[test]
746+
fn from_bytes_with_nul_interior() {
747+
let data = b"1\023\0";
748+
let cstr = CStr::from_bytes_with_nul(data);
749+
assert!(cstr.is_none());
750+
}
673751
}

0 commit comments

Comments
 (0)