Rollup merge of #134143 - nyurik:err-nul, r=dtolnay
Convert `struct FromBytesWithNulError` into enum
This PR renames the former `kind` enum from `FromBytesWithNulErrorKind` to `FromBytesWithNulError`, and removes the original struct.
See https://github.com/rust-lang/libs-team/issues/493
## Possible Changes - TBD
* [x] should the new `enum FromBytesWithNulError` derive `Copy`?
* [ ] should there be any new/changed attributes?
* [x] add some more tests
## Problem
One of `CStr` constructors, `CStr::from_bytes_with_nul(bytes: &[u8])` handles 3 cases:
1. `bytes` has one NULL as the last value - creates CStr
2. `bytes` has no NULL - error
3. `bytes` has a NULL in some other position - error
The 3rd case is error that may require lossy conversion, but the 2nd case can easily be handled by the user code. Unfortunately, this function returns an opaque `FromBytesWithNulError` error in both 2nd and 3rd case, so the user cannot detect just the 2nd case - having to re-implement the entire function and bring in the `memchr` dependency.
## Motivating examples or use cases
In [this code](f86d7a8768/varnish-sys/src/vcl/ws.rs (L158)
), my FFI code needs to copy user's `&[u8]` into a C-allocated memory blob in a NUL-terminated `CStr` format. My code must first validate if `&[u8]` has a trailing NUL (case 1), no NUL (adds one on the fly - case 2), or NUL in the middle (3rd case - error). I had to re-implement `from_bytes_with_nul` and add `memchr`dependency just to handle the 2nd case.
r? `@Amanieu`
This commit is contained in:
commit
229c91bc31
1 changed files with 20 additions and 32 deletions
|
@ -124,37 +124,25 @@ pub struct CStr {
|
||||||
///
|
///
|
||||||
/// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err();
|
/// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
#[stable(feature = "core_c_str", since = "1.64.0")]
|
#[stable(feature = "core_c_str", since = "1.64.0")]
|
||||||
pub struct FromBytesWithNulError {
|
pub enum FromBytesWithNulError {
|
||||||
kind: FromBytesWithNulErrorKind,
|
/// Data provided contains an interior nul byte at byte `position`.
|
||||||
}
|
InteriorNul {
|
||||||
|
/// The position of the interior nul byte.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
position: usize,
|
||||||
enum FromBytesWithNulErrorKind {
|
},
|
||||||
InteriorNul(usize),
|
/// Data provided is not nul terminated.
|
||||||
NotNulTerminated,
|
NotNulTerminated,
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: const stability attributes should not be required here, I think
|
|
||||||
impl FromBytesWithNulError {
|
|
||||||
const fn interior_nul(pos: usize) -> FromBytesWithNulError {
|
|
||||||
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::InteriorNul(pos) }
|
|
||||||
}
|
|
||||||
const fn not_nul_terminated() -> FromBytesWithNulError {
|
|
||||||
FromBytesWithNulError { kind: FromBytesWithNulErrorKind::NotNulTerminated }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
|
#[stable(feature = "frombyteswithnulerror_impls", since = "1.17.0")]
|
||||||
impl Error for FromBytesWithNulError {
|
impl Error for FromBytesWithNulError {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match self.kind {
|
match self {
|
||||||
FromBytesWithNulErrorKind::InteriorNul(..) => {
|
Self::InteriorNul { .. } => "data provided contains an interior nul byte",
|
||||||
"data provided contains an interior nul byte"
|
Self::NotNulTerminated => "data provided is not nul terminated",
|
||||||
}
|
|
||||||
FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,8 +187,8 @@ impl fmt::Display for FromBytesWithNulError {
|
||||||
#[allow(deprecated, deprecated_in_future)]
|
#[allow(deprecated, deprecated_in_future)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str(self.description())?;
|
f.write_str(self.description())?;
|
||||||
if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind {
|
if let Self::InteriorNul { position } = self {
|
||||||
write!(f, " at byte pos {pos}")?;
|
write!(f, " at byte pos {position}")?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -349,25 +337,25 @@ impl CStr {
|
||||||
/// use std::ffi::CStr;
|
/// use std::ffi::CStr;
|
||||||
///
|
///
|
||||||
/// let cstr = CStr::from_bytes_with_nul(b"hello\0");
|
/// let cstr = CStr::from_bytes_with_nul(b"hello\0");
|
||||||
/// assert!(cstr.is_ok());
|
/// assert_eq!(cstr, Ok(c"hello"));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Creating a `CStr` without a trailing nul terminator is an error:
|
/// Creating a `CStr` without a trailing nul terminator is an error:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::ffi::CStr;
|
/// use std::ffi::{CStr, FromBytesWithNulError};
|
||||||
///
|
///
|
||||||
/// let cstr = CStr::from_bytes_with_nul(b"hello");
|
/// let cstr = CStr::from_bytes_with_nul(b"hello");
|
||||||
/// assert!(cstr.is_err());
|
/// assert_eq!(cstr, Err(FromBytesWithNulError::NotNulTerminated));
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Creating a `CStr` with an interior nul byte is an error:
|
/// Creating a `CStr` with an interior nul byte is an error:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use std::ffi::CStr;
|
/// use std::ffi::{CStr, FromBytesWithNulError};
|
||||||
///
|
///
|
||||||
/// let cstr = CStr::from_bytes_with_nul(b"he\0llo\0");
|
/// let cstr = CStr::from_bytes_with_nul(b"he\0llo\0");
|
||||||
/// assert!(cstr.is_err());
|
/// assert_eq!(cstr, Err(FromBytesWithNulError::InteriorNul { position: 2 }));
|
||||||
/// ```
|
/// ```
|
||||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||||
#[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")]
|
#[rustc_const_stable(feature = "const_cstr_methods", since = "1.72.0")]
|
||||||
|
@ -379,8 +367,8 @@ impl CStr {
|
||||||
// of the byte slice.
|
// of the byte slice.
|
||||||
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
|
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
|
||||||
}
|
}
|
||||||
Some(nul_pos) => Err(FromBytesWithNulError::interior_nul(nul_pos)),
|
Some(position) => Err(FromBytesWithNulError::InteriorNul { position }),
|
||||||
None => Err(FromBytesWithNulError::not_nul_terminated()),
|
None => Err(FromBytesWithNulError::NotNulTerminated),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue