Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions library/core/src/slice/ascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ impl [u8] {
}
}

/// If this slice [`is_ascii`](Self::is_ascii), returns it as a mutable slice of
/// [ASCII characters](ascii::Char), otherwise returns `None`.
#[unstable(feature = "ascii_char", issue = "110998")]
#[must_use]
#[inline]
pub const fn as_ascii_mut(&mut self) -> Option<&mut [ascii::Char]> {
if self.is_ascii() {
// SAFETY: Just checked that it's ASCII and since every
// ascii character has the same size, this mutable slice is safe
// to access and it is sound to let the caller write into the
// returned slice because any written ascii character is a valid u8.
Some(unsafe { self.as_ascii_mut_unchecked() })
} else {
None
}
}

/// Converts this slice of bytes into a slice of ASCII characters,
/// without checking whether they're valid.
///
Expand All @@ -55,6 +72,22 @@ impl [u8] {
unsafe { &*ascii_ptr }
}

/// Converts this slice of bytes into a mutable slice of ASCII characters,
/// without checking whether they're valid.
///
/// # Safety
///
/// Every byte in the slice must be in `0..=127`, or else this is undefined behavior.
#[unstable(feature = "ascii_char", issue = "110998")]
#[must_use]
#[inline]
pub const unsafe fn as_ascii_mut_unchecked(&mut self) -> &mut [ascii::Char] {
let byte_ptr: *mut [u8] = self;
let ascii_ptr = byte_ptr as *mut [ascii::Char];
// SAFETY: The caller promised all the bytes are ASCII.
unsafe { &mut *ascii_ptr }
}

/// Checks that two slices are an ASCII case-insensitive match.
///
/// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`,
Expand Down
36 changes: 36 additions & 0 deletions library/core/src/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2811,6 +2811,21 @@ impl str {
self.as_bytes().as_ascii()
}

/// If this string slice [`is_ascii`](Self::is_ascii), returns it as a mutable slice
/// of [ASCII characters](`ascii::Char`), otherwise returns `None`.
#[unstable(feature = "ascii_char", issue = "110998")]
#[must_use]
#[inline]
pub const fn as_ascii_mut(&mut self) -> Option<&mut [ascii::Char]> {
// SAFETY: Every ascii character is an valid u8 and has the same size and
// therefore it is safe to access and modify the slice. In terms of non-ascii
// characters, like ä, are handled by a check in `slice::as_ascii_mut`, that
// checks, if every u8 is an valid ascii character. Non-ascii characters
// consist of multiple u8 bytes and some operations like reversing would
// result in undefined behaviour.
unsafe { self.as_bytes_mut().as_ascii_mut() }
}

/// Converts this string slice into a slice of [ASCII characters](ascii::Char),
/// without checking whether they are valid.
///
Expand All @@ -2832,6 +2847,27 @@ impl str {
unsafe { self.as_bytes().as_ascii_unchecked() }
}

/// If this string slice [`is_ascii`](Self::is_ascii), returns it as a mutable slice
/// of [ASCII characters](`ascii::Char`), otherwise returns `None`.
///
/// # Safety
///
/// Every character in this string must be ASCII, or this is undefined behavior.
#[unstable(feature = "ascii_char", issue = "110998")]
#[must_use]
#[inline]
pub const unsafe fn as_ascii_mut_unchecked(&mut self) -> &mut [ascii::Char] {
Comment thread
RegularCoder239 marked this conversation as resolved.
assert_unsafe_precondition!(
check_library_ub,
"as_ascii_mut_unchecked requires that the string is valid ASCII",
(it: &str = self) => it.is_ascii()
);

// SAFETY: the caller promised that every byte of this string slice
// is ASCII.
unsafe { self.as_bytes_mut().as_ascii_mut_unchecked() }
}

/// Checks that two strings are an ASCII case-insensitive match.
///
/// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`,
Expand Down
Loading