Skip to content

Commit

Permalink
Rollup merge of rust-lang#67569 - Mark-Simulacrum:opt-char-encode, r=…
Browse files Browse the repository at this point in the history
…oli-obk

Clean up unsafety in char::encode_utf8

This originally started as an attempt to allow LLVM to optimize through
encode_utf8 to detect the try_encode_utf8 case (rust-lang#52579, rust-lang#52580), but due to a
typo my conclusion that my optimizations were successful was incorrect.

Furthermore, as far as I can tell, this optimization is probably just not
possible with LLVM today.  This [code](https://rust.godbolt.org/z/JggRj4)
compiles down to a long series of compares, notably, two identical series of
compares. That essentially means that LLVM is today unable to see that these two
ifs are identical and as such can be merged and then realize that no value of
the if condition can result in a call to `please_delete`. As such, for now, we
do not attempt to specifically optimize for that case.
  • Loading branch information
Mark-Simulacrum committed Dec 24, 2019
2 parents b8b9a0f + df4d490 commit cb5e4ab
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 30 deletions.
59 changes: 29 additions & 30 deletions src/libcore/char/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,36 +434,35 @@ impl char {
#[inline]
pub fn encode_utf8(self, dst: &mut [u8]) -> &mut str {
let code = self as u32;
// SAFETY: each arm checks the size of the slice and only uses `get_unchecked` unsafe ops
unsafe {
let len = if code < MAX_ONE_B && !dst.is_empty() {
*dst.get_unchecked_mut(0) = code as u8;
1
} else if code < MAX_TWO_B && dst.len() >= 2 {
*dst.get_unchecked_mut(0) = (code >> 6 & 0x1F) as u8 | TAG_TWO_B;
*dst.get_unchecked_mut(1) = (code & 0x3F) as u8 | TAG_CONT;
2
} else if code < MAX_THREE_B && dst.len() >= 3 {
*dst.get_unchecked_mut(0) = (code >> 12 & 0x0F) as u8 | TAG_THREE_B;
*dst.get_unchecked_mut(1) = (code >> 6 & 0x3F) as u8 | TAG_CONT;
*dst.get_unchecked_mut(2) = (code & 0x3F) as u8 | TAG_CONT;
3
} else if dst.len() >= 4 {
*dst.get_unchecked_mut(0) = (code >> 18 & 0x07) as u8 | TAG_FOUR_B;
*dst.get_unchecked_mut(1) = (code >> 12 & 0x3F) as u8 | TAG_CONT;
*dst.get_unchecked_mut(2) = (code >> 6 & 0x3F) as u8 | TAG_CONT;
*dst.get_unchecked_mut(3) = (code & 0x3F) as u8 | TAG_CONT;
4
} else {
panic!(
"encode_utf8: need {} bytes to encode U+{:X}, but the buffer has {}",
from_u32_unchecked(code).len_utf8(),
code,
dst.len(),
)
};
from_utf8_unchecked_mut(dst.get_unchecked_mut(..len))
}
let len = self.len_utf8();
match (len, &mut dst[..]) {
(1, [a, ..]) => {
*a = code as u8;
}
(2, [a, b, ..]) => {
*a = (code >> 6 & 0x1F) as u8 | TAG_TWO_B;
*b = (code & 0x3F) as u8 | TAG_CONT;
}
(3, [a, b, c, ..]) => {
*a = (code >> 12 & 0x0F) as u8 | TAG_THREE_B;
*b = (code >> 6 & 0x3F) as u8 | TAG_CONT;
*c = (code & 0x3F) as u8 | TAG_CONT;
}
(4, [a, b, c, d, ..]) => {
*a = (code >> 18 & 0x07) as u8 | TAG_FOUR_B;
*b = (code >> 12 & 0x3F) as u8 | TAG_CONT;
*c = (code >> 6 & 0x3F) as u8 | TAG_CONT;
*d = (code & 0x3F) as u8 | TAG_CONT;
}
_ => panic!(
"encode_utf8: need {} bytes to encode U+{:X}, but the buffer has {}",
len,
code,
dst.len(),
),
};
// SAFETY: We just wrote UTF-8 content in, so converting to str is fine.
unsafe { from_utf8_unchecked_mut(&mut dst[..len]) }
}

/// Encodes this character as UTF-16 into the provided `u16` buffer,
Expand Down
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
#![feature(associated_type_bounds)]
#![feature(const_type_id)]
#![feature(const_caller_location)]
#![feature(slice_patterns)]

#[prelude_import]
#[allow(unused)]
Expand Down

0 comments on commit cb5e4ab

Please sign in to comment.