add CStr::from_bytes_until_nul
This adds a member fn that converts a slice into a CStr; it is intended to be safer than from_ptr (which is unsafe and may read out of bounds), and more useful than from_bytes_with_nul (which requires that the caller already know where the nul byte is). feature gate: cstr_from_bytes_until_nul Also add an error type FromBytesUntilNulError for this fn.
This commit is contained in:
parent
1bfe40d11c
commit
d5fe4cad5a
@ -328,6 +328,27 @@ pub fn into_bytes(self) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error indicating that no nul byte was present.
|
||||
///
|
||||
/// A slice used to create a [`CStr`] must contain a nul byte somewhere
|
||||
/// within the slice.
|
||||
///
|
||||
/// This error is created by the [`CStr::from_bytes_until_nul`] method.
|
||||
///
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
pub struct FromBytesUntilNulError(());
|
||||
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
impl Error for FromBytesUntilNulError {}
|
||||
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
impl fmt::Display for FromBytesUntilNulError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "data provided does not contain a nul")
|
||||
}
|
||||
}
|
||||
|
||||
/// An error indicating invalid UTF-8 when converting a [`CString`] into a [`String`].
|
||||
///
|
||||
/// `CString` is just a wrapper over a buffer of bytes with a nul terminator;
|
||||
@ -1239,12 +1260,60 @@ pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a C string wrapper from a byte slice.
|
||||
///
|
||||
/// This method will create a `CStr` from any byte slice that contains at
|
||||
/// least one nul byte. The caller does not need to know or specify where
|
||||
/// the nul byte is located.
|
||||
///
|
||||
/// If the first byte is a nul character, this method will return an
|
||||
/// empty `CStr`. If multiple nul characters are present, the `CStr` will
|
||||
/// end at the first one.
|
||||
///
|
||||
/// If the slice only has a single nul byte at the end, this method is
|
||||
/// equivalent to [`CStr::from_bytes_with_nul`].
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// #![feature(cstr_from_bytes_until_nul)]
|
||||
///
|
||||
/// use std::ffi::CStr;
|
||||
///
|
||||
/// let mut buffer = [0u8; 16];
|
||||
/// unsafe {
|
||||
/// // Here we might call an unsafe C function that writes a string
|
||||
/// // into the buffer.
|
||||
/// let buf_ptr = buffer.as_mut_ptr();
|
||||
/// buf_ptr.write_bytes(b'A', 8);
|
||||
/// }
|
||||
/// // Attempt to extract a C nul-terminated string from the buffer.
|
||||
/// let c_str = CStr::from_bytes_until_nul(&buffer[..]).unwrap();
|
||||
/// assert_eq!(c_str.to_str().unwrap(), "AAAAAAAA");
|
||||
/// ```
|
||||
///
|
||||
#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")]
|
||||
pub fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> {
|
||||
let nul_pos = memchr::memchr(0, bytes);
|
||||
match nul_pos {
|
||||
Some(nul_pos) => {
|
||||
// SAFETY: We know there is a nul byte at nul_pos, so this slice
|
||||
// (ending at the nul byte) is a well-formed C string.
|
||||
let subslice = &bytes[..nul_pos + 1];
|
||||
Ok(unsafe { CStr::from_bytes_with_nul_unchecked(subslice) })
|
||||
}
|
||||
None => Err(FromBytesUntilNulError(())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a C string wrapper from a byte slice.
|
||||
///
|
||||
/// This function will cast the provided `bytes` to a `CStr`
|
||||
/// wrapper after ensuring that the byte slice is nul-terminated
|
||||
/// and does not contain any interior nul bytes.
|
||||
///
|
||||
/// If the nul byte may not be at the end,
|
||||
/// [`CStr::from_bytes_until_nul`] can be used instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
@ -117,6 +117,43 @@ fn from_bytes_with_nul_interior() {
|
||||
assert!(cstr.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cstr_from_bytes_until_nul() {
|
||||
// Test an empty slice. This should fail because it
|
||||
// does not contain a nul byte.
|
||||
let b = b"";
|
||||
assert_eq!(CStr::from_bytes_until_nul(&b[..]), Err(FromBytesUntilNulError(())));
|
||||
|
||||
// Test a non-empty slice, that does not contain a nul byte.
|
||||
let b = b"hello";
|
||||
assert_eq!(CStr::from_bytes_until_nul(&b[..]), Err(FromBytesUntilNulError(())));
|
||||
|
||||
// Test an empty nul-terminated string
|
||||
let b = b"\0";
|
||||
let r = CStr::from_bytes_until_nul(&b[..]).unwrap();
|
||||
assert_eq!(r.to_bytes(), b"");
|
||||
|
||||
// Test a slice with the nul byte in the middle
|
||||
let b = b"hello\0world!";
|
||||
let r = CStr::from_bytes_until_nul(&b[..]).unwrap();
|
||||
assert_eq!(r.to_bytes(), b"hello");
|
||||
|
||||
// Test a slice with the nul byte at the end
|
||||
let b = b"hello\0";
|
||||
let r = CStr::from_bytes_until_nul(&b[..]).unwrap();
|
||||
assert_eq!(r.to_bytes(), b"hello");
|
||||
|
||||
// Test a slice with two nul bytes at the end
|
||||
let b = b"hello\0\0";
|
||||
let r = CStr::from_bytes_until_nul(&b[..]).unwrap();
|
||||
assert_eq!(r.to_bytes(), b"hello");
|
||||
|
||||
// Test a slice containing lots of nul bytes
|
||||
let b = b"\0\0\0\0";
|
||||
let r = CStr::from_bytes_until_nul(&b[..]).unwrap();
|
||||
assert_eq!(r.to_bytes(), b"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_boxed() {
|
||||
let orig: &[u8] = b"Hello, world!\0";
|
||||
|
Loading…
Reference in New Issue
Block a user