Skip to content

Commit c1ccdb7

Browse files
authored
Rollup merge of #130659 - bjoernager:const-char-encode-utf16, r=dtolnay
Support `char::encode_utf16` in const scenarios. Relevant tracking issue: #130660 The method `char::encode_utf16` should be marked "const" to allow compile-time conversions. This PR additionally rewrites the `encode_utf16_raw` function for better readability whilst also reducing the amount of unsafe code. try-job: x86_64-msvc
2 parents 23393de + f48c5ec commit c1ccdb7

2 files changed

Lines changed: 36 additions & 25 deletions

File tree

‎library/core/src/char/methods.rs‎

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -638,8 +638,7 @@ impl char {
638638
#[rustc_const_stable(feature = "const_char_len_utf", since = "1.52.0")]
639639
#[inline]
640640
pubconstfnlen_utf16(self) -> usize{
641-
let ch = selfasu32;
642-
if(ch &0xFFFF) == ch {1}else{2}
641+
len_utf16(selfasu32)
643642
}
644643

645644
/// Encodes this character as UTF-8 into the provided byte buffer,
@@ -709,8 +708,9 @@ impl char {
709708
/// '𝕊'.encode_utf16(&mut b);
710709
/// ```
711710
#[stable(feature = "unicode_encode_char", since = "1.15.0")]
711+
#[rustc_const_unstable(feature = "const_char_encode_utf16", issue = "130660")]
712712
#[inline]
713-
pubfnencode_utf16(self,dst:&mut[u16]) -> &mut[u16]{
713+
pubconstfnencode_utf16(self,dst:&mut[u16]) -> &mut[u16]{
714714
encode_utf16_raw(selfasu32, dst)
715715
}
716716

@@ -1747,7 +1747,12 @@ const fn len_utf8(code: u32) -> usize {
17471747
}
17481748
}
17491749

1750-
/// Encodes a raw u32 value as UTF-8 into the provided byte buffer,
1750+
#[inline]
1751+
constfnlen_utf16(code:u32) -> usize{
1752+
if(code &0xFFFF) == code {1}else{2}
1753+
}
1754+
1755+
/// Encodes a raw `u32` value as UTF-8 into the provided byte buffer,
17511756
/// and then returns the subslice of the buffer that contains the encoded character.
17521757
///
17531758
/// Unlike `char::encode_utf8`, this method also handles codepoints in the surrogate range.
@@ -1801,7 +1806,7 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
18011806
unsafe{ slice::from_raw_parts_mut(dst.as_mut_ptr(), len)}
18021807
}
18031808

1804-
/// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer,
1809+
/// Encodes a raw `u32` value as UTF-16 into the provided `u16` buffer,
18051810
/// and then returns the subslice of the buffer that contains the encoded character.
18061811
///
18071812
/// Unlike `char::encode_utf16`, this method also handles codepoints in the surrogate range.
@@ -1812,28 +1817,33 @@ pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] {
18121817
/// Panics if the buffer is not large enough.
18131818
/// A buffer of length 2 is large enough to encode any `char`.
18141819
#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")]
1820+
#[rustc_const_unstable(feature = "const_char_encode_utf16", issue = "130660")]
18151821
#[doc(hidden)]
18161822
#[inline]
1817-
pubfnencode_utf16_raw(mutcode:u32,dst:&mut[u16]) -> &mut[u16]{
1818-
// SAFETY: each arm checks whether there are enough bits to write into
1819-
unsafe{
1820-
if(code &0xFFFF) == code && !dst.is_empty(){
1821-
// The BMP falls through
1822-
*dst.get_unchecked_mut(0) = code asu16;
1823-
slice::from_raw_parts_mut(dst.as_mut_ptr(),1)
1824-
}elseif dst.len() >= 2{
1825-
// Supplementary planes break into surrogates.
1823+
pubconstfnencode_utf16_raw(mutcode:u32,dst:&mut[u16]) -> &mut[u16]{
1824+
constfnpanic_at_const(_code:u32,_len:usize,_dst_len:usize){
1825+
// Note that we cannot format in constant expressions.
1826+
panic!("encode_utf16: buffer does not have enough bytes to encode code point");
1827+
}
1828+
fnpanic_at_rt(code:u32,len:usize,dst_len:usize){
1829+
panic!(
1830+
"encode_utf16: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}",
1831+
);
1832+
}
1833+
let len = len_utf16(code);
1834+
match(len,&mut*dst){
1835+
(1,[a, ..]) => {
1836+
*a = code asu16;
1837+
}
1838+
(2,[a, b, ..]) => {
18261839
code -= 0x1_0000;
1827-
*dst.get_unchecked_mut(0) = 0xD800 | ((code >> 10)asu16);
1828-
*dst.get_unchecked_mut(1) = 0xDC00 | ((code asu16)&0x3FF);
1829-
slice::from_raw_parts_mut(dst.as_mut_ptr(),2)
1830-
}else{
1831-
panic!(
1832-
"encode_utf16: need {} units to encode U+{:X}, but the buffer has {}",
1833-
char::from_u32_unchecked(code).len_utf16(),
1834-
code,
1835-
dst.len(),
1836-
)
1840+
1841+
*a = (code >> 10)asu16 | 0xD800;
1842+
*b = (code &0x3FF)asu16 | 0xDC00;
18371843
}
1838-
}
1844+
// FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly.
1845+
_ => const_eval_select((code, len, dst.len()), panic_at_const, panic_at_rt),
1846+
};
1847+
// SAFETY: `<&mut [u16]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds.
1848+
unsafe{ slice::from_raw_parts_mut(dst.as_mut_ptr(), len)}
18391849
}

‎library/core/src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
#![feature(const_bigint_helper_methods)]
120120
#![feature(const_black_box)]
121121
#![feature(const_cell_into_inner)]
122+
#![feature(const_char_encode_utf16)]
122123
#![feature(const_char_encode_utf8)]
123124
#![feature(const_eval_select)]
124125
#![feature(const_exact_div)]

0 commit comments

Comments
 (0)