From 3182cdf9baf8ed9e8ae24f4742ee5d3d01c2b54a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 May 2020 11:53:50 +0200 Subject: wtf8: use encode_utf8_raw --- src/libstd/lib.rs | 1 + src/libstd/sys_common/wtf8.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/libstd') diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index cc3e613fa3d..3e3c1fd9026 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -247,6 +247,7 @@ #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] +#![feature(char_internals)] #![feature(clamp)] #![feature(concat_idents)] #![feature(const_cstr_unchecked)] diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index a98407da448..90bbf4afd1a 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -201,9 +201,8 @@ impl Wtf8Buf { /// Copied from String::push /// This does **not** include the WTF-8 concatenation check. fn push_code_point_unchecked(&mut self, code_point: CodePoint) { - let c = unsafe { char::from_u32_unchecked(code_point.value) }; let mut bytes = [0; 4]; - let bytes = c.encode_utf8(&mut bytes).as_bytes(); + let bytes = char::encode_utf8_raw(code_point.value, &mut bytes).as_bytes(); self.bytes.extend_from_slice(bytes) } -- cgit 1.4.1-3-g733a5 From 9c627c33dde998cfe42bcde07e1c5692370daf63 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 May 2020 12:08:55 +0200 Subject: also expose and use encode_utf16_raw for wtf8 --- src/libcore/char/methods.rs | 59 +++++++++++++++++++++++++++---------------- src/libcore/char/mod.rs | 2 ++ src/libstd/sys_common/wtf8.rs | 3 +-- 3 files changed, 40 insertions(+), 24 deletions(-) (limited to 'src/libstd') diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs index 112e7e38e41..b1b3c70efb1 100644 --- a/src/libcore/char/methods.rs +++ b/src/libcore/char/methods.rs @@ -701,28 +701,7 @@ impl char { #[stable(feature = "unicode_encode_char", since = "1.15.0")] #[inline] pub fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] { - let mut code = self as u32; - // SAFETY: each arm checks whether there are enough bits to write into - unsafe { - if (code & 0xFFFF) == code && !dst.is_empty() { - // The BMP falls through (assuming non-surrogate, as it should) - *dst.get_unchecked_mut(0) = code as u16; - slice::from_raw_parts_mut(dst.as_mut_ptr(), 1) - } else if dst.len() >= 2 { - // Supplementary planes break into surrogates. - code -= 0x1_0000; - *dst.get_unchecked_mut(0) = 0xD800 | ((code >> 10) as u16); - *dst.get_unchecked_mut(1) = 0xDC00 | ((code as u16) & 0x3FF); - slice::from_raw_parts_mut(dst.as_mut_ptr(), 2) - } else { - panic!( - "encode_utf16: need {} units to encode U+{:X}, but the buffer has {}", - from_u32_unchecked(code).len_utf16(), - code, - dst.len(), - ) - } - } + encode_utf16_raw(self as u32, dst) } /// Returns `true` if this `char` has the `Alphabetic` property. @@ -1692,3 +1671,39 @@ pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut str { // SAFETY: We just wrote UTF-8 content in, so converting to str is fine. unsafe { from_utf8_unchecked_mut(&mut dst[..len]) } } + +/// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer, +/// and then returns the subslice of the buffer that contains the encoded character. +/// +/// Unlike `char::encode_utf16`, this method can be called on codepoints in the surrogate range. +/// +/// # Panics +/// +/// Panics if the buffer is not large enough. +/// A buffer of length 2 is large enough to encode any `char`. +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +#[doc(hidden)] +#[inline] +pub fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { + // SAFETY: each arm checks whether there are enough bits to write into + unsafe { + if (code & 0xFFFF) == code && !dst.is_empty() { + // The BMP falls through (assuming non-surrogate, as it should) + *dst.get_unchecked_mut(0) = code as u16; + slice::from_raw_parts_mut(dst.as_mut_ptr(), 1) + } else if dst.len() >= 2 { + // Supplementary planes break into surrogates. + code -= 0x1_0000; + *dst.get_unchecked_mut(0) = 0xD800 | ((code >> 10) as u16); + *dst.get_unchecked_mut(1) = 0xDC00 | ((code as u16) & 0x3FF); + slice::from_raw_parts_mut(dst.as_mut_ptr(), 2) + } else { + panic!( + "encode_utf16: need {} units to encode U+{:X}, but the buffer has {}", + from_u32_unchecked(code).len_utf16(), + code, + dst.len(), + ) + } + } +} diff --git a/src/libcore/char/mod.rs b/src/libcore/char/mod.rs index 40b429b7496..1b4e906e4e4 100644 --- a/src/libcore/char/mod.rs +++ b/src/libcore/char/mod.rs @@ -39,6 +39,8 @@ pub use crate::unicode::UNICODE_VERSION; // perma-unstable re-exports #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +pub use self::methods::encode_utf16_raw; +#[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] pub use self::methods::encode_utf8_raw; use crate::fmt::{self, Write}; diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index 90bbf4afd1a..9f589c93ae5 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -828,8 +828,7 @@ impl<'a> Iterator for EncodeWide<'a> { let mut buf = [0; 2]; self.code_points.next().map(|code_point| { - let c = unsafe { char::from_u32_unchecked(code_point.value) }; - let n = c.encode_utf16(&mut buf).len(); + let n = char::encode_utf16_raw(code_point.value, &mut buf).len(); if n == 2 { self.extra = buf[1]; } -- cgit 1.4.1-3-g733a5 From 0fb6e63c0438ace4ad9d496376af955c0baacf04 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 May 2020 17:13:07 +0200 Subject: encode_utf8_raw is not always valid UTF-8; clarify comments --- src/libcore/char/methods.rs | 19 ++++++++++++------- src/libstd/sys_common/wtf8.rs | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src/libstd') diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs index b1b3c70efb1..bf09b28ff69 100644 --- a/src/libcore/char/methods.rs +++ b/src/libcore/char/methods.rs @@ -661,7 +661,8 @@ impl char { #[stable(feature = "unicode_encode_char", since = "1.15.0")] #[inline] pub fn encode_utf8(self, dst: &mut [u8]) -> &mut str { - encode_utf8_raw(self as u32, dst) + // SAFETY: `char` is not a surrogate, so this is valid UTF-8. + unsafe { from_utf8_unchecked_mut(encode_utf8_raw(self as u32, dst)) } } /// Encodes this character as UTF-16 into the provided `u16` buffer, @@ -1631,7 +1632,11 @@ fn len_utf8(code: u32) -> usize { /// Encodes a raw u32 value as UTF-8 into the provided byte buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// -/// Unlike `char::encode_utf8`, this method can be called on codepoints in the surrogate range. +/// Unlike `char::encode_utf8`, this method also handles codepoints in the surrogate range. +/// (Creating a `char` in the surrogate range is UB.) +/// The result is valid [generalized UTF-8] but not valid UTF-8. +/// +/// [generalized UTF-8]: https://simonsapin.github.io/wtf-8/#generalized-utf8 /// /// # Panics /// @@ -1640,7 +1645,7 @@ fn len_utf8(code: u32) -> usize { #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] #[doc(hidden)] #[inline] -pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut str { +pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { let len = len_utf8(code); match (len, &mut dst[..]) { (1, [a, ..]) => { @@ -1668,14 +1673,14 @@ pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut str { dst.len(), ), }; - // SAFETY: We just wrote UTF-8 content in, so converting to str is fine. - unsafe { from_utf8_unchecked_mut(&mut dst[..len]) } + &mut dst[..len] } /// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer, /// and then returns the subslice of the buffer that contains the encoded character. /// -/// Unlike `char::encode_utf16`, this method can be called on codepoints in the surrogate range. +/// Unlike `char::encode_utf16`, this method also handles codepoints in the surrogate range. +/// (Creating a `char` in the surrogate range is UB.) /// /// # Panics /// @@ -1688,7 +1693,7 @@ pub fn encode_utf16_raw(mut code: u32, dst: &mut [u16]) -> &mut [u16] { // SAFETY: each arm checks whether there are enough bits to write into unsafe { if (code & 0xFFFF) == code && !dst.is_empty() { - // The BMP falls through (assuming non-surrogate, as it should) + // The BMP falls through *dst.get_unchecked_mut(0) = code as u16; slice::from_raw_parts_mut(dst.as_mut_ptr(), 1) } else if dst.len() >= 2 { diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index 9f589c93ae5..ccb54b7e68d 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -202,7 +202,7 @@ impl Wtf8Buf { /// This does **not** include the WTF-8 concatenation check. fn push_code_point_unchecked(&mut self, code_point: CodePoint) { let mut bytes = [0; 4]; - let bytes = char::encode_utf8_raw(code_point.value, &mut bytes).as_bytes(); + let bytes = char::encode_utf8_raw(code_point.value, &mut bytes); self.bytes.extend_from_slice(bytes) } -- cgit 1.4.1-3-g733a5