alloc/wtf8/mod.rs
1//! Heap-allocated counterpart to core `wtf8` module.
2#![unstable(
3 feature = "wtf8_internals",
4 issue = "none",
5 reason = "this is internal code for representing OsStr on some platforms and not a public API"
6)]
7// rustdoc bug: doc(hidden) on the module won't stop types in the module from showing up in trait
8// implementations, so, we'll have to add more doc(hidden)s anyway
9#![doc(hidden)]
10
11// Note: This module is also included in the alloctests crate using #[path] to
12// run the tests. See the comment there for an explanation why this is the case.
13
14#[cfg(test)]
15mod tests;
16
17use core::char::encode_utf8_raw;
18use core::hash::{Hash, Hasher};
19pub use core::wtf8::{CodePoint, Wtf8};
20#[cfg(not(test))]
21pub use core::wtf8::{EncodeWide, Wtf8CodePoints};
22use core::{fmt, mem, ops, str};
23
24use crate::borrow::{Cow, ToOwned};
25use crate::boxed::Box;
26use crate::collections::TryReserveError;
27#[cfg(not(test))]
28use crate::rc::Rc;
29use crate::string::String;
30#[cfg(all(not(test), target_has_atomic = "ptr"))]
31use crate::sync::Arc;
32use crate::vec::Vec;
33
34/// An owned, growable string of [well-formed WTF-8](https://wtf-8.codeberg.page/#well-formed) data.
35///
36/// Similar to `String`, but can additionally contain surrogate code points
37/// if they’re not in a surrogate pair.
38#[derive(Eq, PartialEq, Ord, PartialOrd, Clone)]
39#[doc(hidden)]
40pub struct Wtf8Buf {
41 bytes: Vec<u8>,
42
43 /// Do we know that `bytes` holds a valid UTF-8 encoding? We can easily
44 /// know this if we're constructed from a `String` or `&str`.
45 ///
46 /// It is possible for `bytes` to have valid UTF-8 without this being
47 /// set, such as when we're concatenating `&Wtf8`'s and surrogates become
48 /// paired, as we don't bother to rescan the entire string.
49 is_known_utf8: bool,
50}
51
52impl ops::Deref for Wtf8Buf {
53 type Target = Wtf8;
54
55 fn deref(&self) -> &Wtf8 {
56 self.as_slice()
57 }
58}
59
60impl ops::DerefMut for Wtf8Buf {
61 fn deref_mut(&mut self) -> &mut Wtf8 {
62 self.as_mut_slice()
63 }
64}
65
66/// Formats the string in double quotes, with characters escaped according to
67/// [`char::escape_debug`] and unpaired surrogates represented as `\u{xxxx}`,
68/// where each `x` is a hexadecimal digit.
69///
70/// For example, the code units [U+0061, U+D800, U+000A] are formatted as
71/// `"a\u{D800}\n"`.
72impl fmt::Debug for Wtf8Buf {
73 #[inline]
74 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
75 fmt::Debug::fmt(&**self, formatter)
76 }
77}
78
79/// Formats the string with unpaired surrogates substituted with the replacement
80/// character, U+FFFD.
81impl fmt::Display for Wtf8Buf {
82 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
83 if let Some(s) = self.as_known_utf8() {
84 fmt::Display::fmt(s, formatter)
85 } else {
86 fmt::Display::fmt(&**self, formatter)
87 }
88 }
89}
90
91#[cfg_attr(test, allow(dead_code))]
92impl Wtf8Buf {
93 /// Creates a new, empty WTF-8 string.
94 #[inline]
95 pub fn new() -> Wtf8Buf {
96 Wtf8Buf { bytes: Vec::new(), is_known_utf8: true }
97 }
98
99 /// Creates a new, empty WTF-8 string with pre-allocated capacity for `capacity` bytes.
100 #[inline]
101 pub fn with_capacity(capacity: usize) -> Wtf8Buf {
102 Wtf8Buf { bytes: Vec::with_capacity(capacity), is_known_utf8: true }
103 }
104
105 /// Creates a WTF-8 string from a WTF-8 byte vec.
106 ///
107 /// # Safety
108 ///
109 /// `value` must contain [well-formed WTF-8](https://wtf-8.codeberg.page/#well-formed).
110 #[inline]
111 pub unsafe fn from_bytes_unchecked(value: Vec<u8>) -> Wtf8Buf {
112 Wtf8Buf { bytes: value, is_known_utf8: false }
113 }
114
115 /// Creates a WTF-8 string from a UTF-8 `String`.
116 ///
117 /// This takes ownership of the `String` and does not copy.
118 ///
119 /// Since WTF-8 is a superset of UTF-8, this always succeeds.
120 #[inline]
121 pub const fn from_string(string: String) -> Wtf8Buf {
122 Wtf8Buf { bytes: string.into_bytes(), is_known_utf8: true }
123 }
124
125 /// Creates a WTF-8 string from a UTF-8 `&str` slice.
126 ///
127 /// This copies the content of the slice.
128 ///
129 /// Since WTF-8 is a superset of UTF-8, this always succeeds.
130 #[inline]
131 pub fn from_str(s: &str) -> Wtf8Buf {
132 Wtf8Buf { bytes: s.as_bytes().to_vec(), is_known_utf8: true }
133 }
134
135 pub fn clear(&mut self) {
136 self.bytes.clear();
137 self.is_known_utf8 = true;
138 }
139
140 /// Creates a WTF-8 string from a potentially ill-formed UTF-16 slice of 16-bit code units.
141 ///
142 /// This is lossless: calling `.encode_wide()` on the resulting string
143 /// will always return the original code units.
144 pub fn from_wide(v: &[u16]) -> Wtf8Buf {
145 let mut string = Wtf8Buf::with_capacity(v.len());
146 for item in char::decode_utf16(v.iter().cloned()) {
147 match item {
148 Ok(ch) => string.push_char(ch),
149 Err(surrogate) => {
150 let surrogate = surrogate.unpaired_surrogate();
151 // SAFETY: Surrogates are known to be in the code point range.
152 let code_point = unsafe { CodePoint::from_u32_unchecked(surrogate as u32) };
153 // The string will now contain an unpaired surrogate.
154 string.is_known_utf8 = false;
155 // SAFETY: `decode_utf16` reports only unpaired surrogates here,
156 // so this code point cannot form a surrogate pair with the
157 // preceding and succeeding contents. The existing buffer is
158 // well-formed WTF-8, and appending this encoded surrogate
159 // preserves that invariant.
160 unsafe {
161 string.push_code_point_unchecked(code_point);
162 }
163 }
164 }
165 }
166 string
167 }
168
169 /// Appends the given `char` to the end of this string.
170 /// This does **not** include the WTF-8 concatenation check or `is_known_utf8` check.
171 /// Copied from String::push.
172 ///
173 /// # Safety
174 ///
175 /// `self` must contain [well-formed WTF-8](https://wtf-8.codeberg.page/#well-formed),
176 /// and appending `code_point` must preserve that invariant. In particular,
177 /// `code_point` must not be a trailing surrogate if `self` ends with a leading surrogate.
178 ///
179 /// If `self.is_known_utf8` is true, `code_point` must not be a surrogate.
180 unsafe fn push_code_point_unchecked(&mut self, code_point: CodePoint) {
181 let mut bytes = [0; char::MAX_LEN_UTF8];
182 let bytes = encode_utf8_raw(code_point.to_u32(), &mut bytes);
183 self.bytes.extend_from_slice(bytes)
184 }
185
186 #[inline]
187 pub fn as_slice(&self) -> &Wtf8 {
188 // SAFETY: `self` maintains `bytes` as well-formed WTF-8.
189 unsafe { Wtf8::from_bytes_unchecked(&self.bytes) }
190 }
191
192 #[inline]
193 pub fn as_mut_slice(&mut self) -> &mut Wtf8 {
194 // SAFETY: `self` maintains `bytes` as well-formed WTF-8.
195 unsafe { Wtf8::from_mut_bytes_unchecked(&mut self.bytes) }
196 }
197
198 /// Converts the string to UTF-8 without validation, if it was created from
199 /// valid UTF-8.
200 #[inline]
201 fn as_known_utf8(&self) -> Option<&str> {
202 if self.is_known_utf8 {
203 // SAFETY: The buffer is known to be valid UTF-8.
204 Some(unsafe { str::from_utf8_unchecked(self.as_bytes()) })
205 } else {
206 None
207 }
208 }
209
210 /// Reserves capacity for at least `additional` more bytes to be inserted
211 /// in the given `Wtf8Buf`.
212 /// The collection may reserve more space to avoid frequent reallocations.
213 ///
214 /// # Panics
215 ///
216 /// Panics if the new capacity exceeds `isize::MAX` bytes.
217 #[inline]
218 pub fn reserve(&mut self, additional: usize) {
219 self.bytes.reserve(additional)
220 }
221
222 /// Tries to reserve capacity for at least `additional` more bytes to be
223 /// inserted in the given `Wtf8Buf`. The `Wtf8Buf` may reserve more space to
224 /// avoid frequent reallocations. After calling `try_reserve`, capacity will
225 /// be greater than or equal to `self.len() + additional`. Does nothing if
226 /// capacity is already sufficient. This method preserves the contents even
227 /// if an error occurs.
228 ///
229 /// # Errors
230 ///
231 /// If the capacity overflows, or the allocator reports a failure, then an error
232 /// is returned.
233 #[inline]
234 pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
235 self.bytes.try_reserve(additional)
236 }
237
238 #[inline]
239 pub fn reserve_exact(&mut self, additional: usize) {
240 self.bytes.reserve_exact(additional)
241 }
242
243 /// Tries to reserve the minimum capacity for exactly `additional` more
244 /// bytes to be inserted in the given `Wtf8Buf`. After calling
245 /// `try_reserve_exact`, capacity will be greater than or equal to
246 /// `self.len() + additional` if it returns `Ok(())`.
247 /// Does nothing if the capacity is already sufficient.
248 ///
249 /// Note that the allocator may give the `Wtf8Buf` more space than it
250 /// requests. Therefore, capacity can not be relied upon to be precisely
251 /// minimal. Prefer [`try_reserve`] if future insertions are expected.
252 ///
253 /// [`try_reserve`]: Wtf8Buf::try_reserve
254 ///
255 /// # Errors
256 ///
257 /// If the capacity overflows, or the allocator reports a failure, then an error
258 /// is returned.
259 #[inline]
260 pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
261 self.bytes.try_reserve_exact(additional)
262 }
263
264 #[inline]
265 pub fn shrink_to_fit(&mut self) {
266 self.bytes.shrink_to_fit()
267 }
268
269 #[inline]
270 pub fn shrink_to(&mut self, min_capacity: usize) {
271 self.bytes.shrink_to(min_capacity)
272 }
273
274 #[inline]
275 pub fn leak<'a>(self) -> &'a mut Wtf8 {
276 // SAFETY: `self` maintains `bytes` as well-formed WTF-8.
277 unsafe { Wtf8::from_mut_bytes_unchecked(self.bytes.leak()) }
278 }
279
280 /// Returns the number of bytes that this string buffer can hold without reallocating.
281 #[inline]
282 pub fn capacity(&self) -> usize {
283 self.bytes.capacity()
284 }
285
286 /// Append a UTF-8 slice at the end of the string.
287 #[inline]
288 pub fn push_str(&mut self, other: &str) {
289 self.bytes.extend_from_slice(other.as_bytes())
290 }
291
292 /// Append a WTF-8 slice at the end of the string.
293 ///
294 /// This replaces newly paired surrogates at the boundary
295 /// with a supplementary code point,
296 /// like concatenating ill-formed UTF-16 strings effectively would.
297 #[inline]
298 pub fn push_wtf8(&mut self, other: &Wtf8) {
299 match ((*self).final_lead_surrogate(), other.initial_trail_surrogate()) {
300 // Replace newly paired surrogates by a supplementary code point.
301 (Some(lead), Some(trail)) => {
302 let len_without_lead_surrogate = self.len() - 3;
303 self.bytes.truncate(len_without_lead_surrogate);
304 let other_without_trail_surrogate = &other.as_bytes()[3..];
305 // 4 bytes for the supplementary code point
306 self.bytes.reserve(4 + other_without_trail_surrogate.len());
307 self.push_char(decode_surrogate_pair(lead, trail));
308 self.bytes.extend_from_slice(other_without_trail_surrogate);
309 }
310 _ => {
311 // If we'll be pushing a string containing a surrogate, we may
312 // no longer have UTF-8.
313 if self.is_known_utf8 && other.next_surrogate(0).is_some() {
314 self.is_known_utf8 = false;
315 }
316
317 self.bytes.extend_from_slice(other.as_bytes());
318 }
319 }
320 }
321
322 /// Append a Unicode scalar value at the end of the string.
323 #[inline]
324 pub fn push_char(&mut self, c: char) {
325 // SAFETY: It's always safe to push a char.
326 unsafe { self.push_code_point_unchecked(CodePoint::from_char(c)) }
327 }
328
329 /// Append a code point at the end of the string.
330 ///
331 /// This replaces newly paired surrogates at the boundary
332 /// with a supplementary code point,
333 /// like concatenating ill-formed UTF-16 strings effectively would.
334 #[inline]
335 pub fn push(&mut self, code_point: CodePoint) {
336 if let Some(trail) = code_point.to_trail_surrogate() {
337 if let Some(lead) = (*self).final_lead_surrogate() {
338 let len_without_lead_surrogate = self.len() - 3;
339 self.bytes.truncate(len_without_lead_surrogate);
340 self.push_char(decode_surrogate_pair(lead, trail));
341 return;
342 }
343
344 // We're pushing a trailing surrogate.
345 self.is_known_utf8 = false;
346 } else if code_point.to_lead_surrogate().is_some() {
347 // We're pushing a leading surrogate.
348 self.is_known_utf8 = false;
349 }
350
351 // SAFETY: We have checked that no newly paired surrogates at the boundary.
352 unsafe { self.push_code_point_unchecked(code_point) }
353 }
354
355 /// Shortens a string to the specified length.
356 ///
357 /// If `new_len` is greater than the string's current length, this has no
358 /// effect.
359 ///
360 /// # Panics
361 ///
362 /// Panics if `new_len` does not lie on a code point boundary.
363 #[inline]
364 pub fn truncate(&mut self, new_len: usize) {
365 if new_len <= self.len() {
366 assert!(self.is_code_point_boundary(new_len));
367 self.bytes.truncate(new_len)
368 }
369 }
370
371 /// Consumes the WTF-8 string and tries to convert it to a vec of bytes.
372 #[inline]
373 pub fn into_bytes(self) -> Vec<u8> {
374 self.bytes
375 }
376
377 /// Consumes the WTF-8 string and tries to convert it to UTF-8.
378 ///
379 /// This does not copy the data.
380 ///
381 /// If the contents are not well-formed UTF-8
382 /// (that is, if the string contains surrogates),
383 /// the original WTF-8 string is returned instead.
384 pub fn into_string(self) -> Result<String, Wtf8Buf> {
385 if self.is_known_utf8 || self.next_surrogate(0).is_none() {
386 // SAFETY: We have checked that `self.bytes` contains valid UTF-8.
387 Ok(unsafe { String::from_utf8_unchecked(self.bytes) })
388 } else {
389 Err(self)
390 }
391 }
392
393 /// Consumes the WTF-8 string and converts it lossily to UTF-8.
394 ///
395 /// This does not copy the data (but may overwrite parts of it in place).
396 ///
397 /// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”)
398 pub fn into_string_lossy(mut self) -> String {
399 if !self.is_known_utf8 {
400 let mut pos = 0;
401 while let Some((surrogate_pos, _)) = self.next_surrogate(pos) {
402 pos = surrogate_pos + 3;
403 // Surrogates and the replacement character are all 3 bytes, so
404 // they can substituted in-place.
405 self.bytes[surrogate_pos..pos].copy_from_slice("\u{FFFD}".as_bytes());
406 }
407 }
408 // SAFETY: Now `self.bytes` contains valid UTF-8.
409 unsafe { String::from_utf8_unchecked(self.bytes) }
410 }
411
412 /// Converts this `Wtf8Buf` into a boxed `Wtf8`.
413 #[inline]
414 pub fn into_box(self) -> Box<Wtf8> {
415 // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`, and
416 // `self.bytes.into_boxed_slice()` returns a `Box<[u8]>`.
417 // Therefore, transmuting `Box<[u8]>` to `Box<Wtf8>` is safe.
418 unsafe { mem::transmute(self.bytes.into_boxed_slice()) }
419 }
420
421 /// Converts a `Box<Wtf8>` into a `Wtf8Buf`.
422 pub fn from_box(boxed: Box<Wtf8>) -> Wtf8Buf {
423 // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`, and `boxed` is
424 // a `Box<Wtf8>`. Therefore, transmuting `Box<Wtf8>` to `Box<[u8]>` is safe.
425 let bytes: Box<[u8]> = unsafe { mem::transmute(boxed) };
426 Wtf8Buf { bytes: bytes.into_vec(), is_known_utf8: false }
427 }
428
429 /// Provides plumbing to core `Vec::extend_from_slice`.
430 /// More well behaving alternative to allowing outer types
431 /// full mutable access to the core `Vec`.
432 ///
433 /// # Safety
434 ///
435 /// `self` and `other` must contain [well-formed WTF-8](https://wtf-8.codeberg.page/#well-formed),
436 /// and appending `other` to `self` must preserve that invariant.
437 /// In particular, `self` must not end with a leading surrogate,
438 /// or `other` must not start with a trailing surrogate.
439 #[inline]
440 pub unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) {
441 self.bytes.extend_from_slice(other);
442 self.is_known_utf8 = false;
443 }
444}
445
446/// Creates a new WTF-8 string from an iterator of code points.
447///
448/// This replaces surrogate code point pairs with supplementary code points,
449/// like concatenating ill-formed UTF-16 strings effectively would.
450impl FromIterator<CodePoint> for Wtf8Buf {
451 fn from_iter<I: IntoIterator<Item = CodePoint>>(iter: I) -> Wtf8Buf {
452 let mut string = Wtf8Buf::new();
453 string.extend(iter);
454 string
455 }
456}
457
458/// Append code points from an iterator to the string.
459///
460/// This replaces surrogate code point pairs with supplementary code points,
461/// like concatenating ill-formed UTF-16 strings effectively would.
462impl Extend<CodePoint> for Wtf8Buf {
463 fn extend<I: IntoIterator<Item = CodePoint>>(&mut self, iter: I) {
464 let iterator = iter.into_iter();
465 let (low, _high) = iterator.size_hint();
466 // Lower bound of one byte per code point (ASCII only)
467 self.bytes.reserve(low);
468 iterator.for_each(move |code_point| self.push(code_point));
469 }
470
471 #[inline]
472 fn extend_one(&mut self, code_point: CodePoint) {
473 self.push(code_point);
474 }
475
476 #[inline]
477 fn extend_reserve(&mut self, additional: usize) {
478 // Lower bound of one byte per code point (ASCII only)
479 self.bytes.reserve(additional);
480 }
481}
482
483/// Creates an owned `Wtf8Buf` from a borrowed `Wtf8`.
484pub(super) fn to_owned(slice: &Wtf8) -> Wtf8Buf {
485 Wtf8Buf { bytes: slice.as_bytes().to_vec(), is_known_utf8: false }
486}
487
488/// Lossily converts the string to UTF-8.
489/// Returns a UTF-8 `&str` slice if the contents are well-formed in UTF-8.
490///
491/// Surrogates are replaced with `"\u{FFFD}"` (the replacement character “�”).
492///
493/// This only copies the data if necessary (if it contains any surrogate).
494pub(super) fn to_string_lossy(slice: &Wtf8) -> Cow<'_, str> {
495 let Some((surrogate_pos, _)) = slice.next_surrogate(0) else {
496 // SAFETY: `next_surrogate` found no surrogate, so the well-formed WTF-8
497 // bytes are valid UTF-8.
498 return Cow::Borrowed(unsafe { str::from_utf8_unchecked(slice.as_bytes()) });
499 };
500 let wtf8_bytes = slice.as_bytes();
501 let mut utf8_bytes = Vec::with_capacity(slice.len());
502 utf8_bytes.extend_from_slice(&wtf8_bytes[..surrogate_pos]);
503 utf8_bytes.extend_from_slice("\u{FFFD}".as_bytes());
504 let mut pos = surrogate_pos + 3;
505 loop {
506 match slice.next_surrogate(pos) {
507 Some((surrogate_pos, _)) => {
508 utf8_bytes.extend_from_slice(&wtf8_bytes[pos..surrogate_pos]);
509 utf8_bytes.extend_from_slice("\u{FFFD}".as_bytes());
510 pos = surrogate_pos + 3;
511 }
512 None => {
513 utf8_bytes.extend_from_slice(&wtf8_bytes[pos..]);
514 // SAFETY: Every surrogate was replaced with `"\u{FFFD}"`,
515 // and the remaining bytes are valid UTF-8, so `utf8_bytes` is valid UTF-8.
516 return Cow::Owned(unsafe { String::from_utf8_unchecked(utf8_bytes) });
517 }
518 }
519 }
520}
521
522#[inline]
523pub(super) fn clone_into(slice: &Wtf8, buf: &mut Wtf8Buf) {
524 buf.is_known_utf8 = false;
525 slice.as_bytes().clone_into(&mut buf.bytes);
526}
527
528#[cfg(not(test))]
529impl Wtf8 {
530 #[rustc_allow_incoherent_impl]
531 pub fn to_owned(&self) -> Wtf8Buf {
532 to_owned(self)
533 }
534
535 #[rustc_allow_incoherent_impl]
536 pub fn clone_into(&self, buf: &mut Wtf8Buf) {
537 clone_into(self, buf)
538 }
539
540 #[rustc_allow_incoherent_impl]
541 pub fn to_string_lossy(&self) -> Cow<'_, str> {
542 to_string_lossy(self)
543 }
544
545 #[rustc_allow_incoherent_impl]
546 pub fn into_box(&self) -> Box<Wtf8> {
547 let boxed: Box<[u8]> = self.as_bytes().into();
548 // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`, and `boxed` is
549 // a `Box<[u8]>`. Therefore, transmuting `Box<[u8]>` to `Box<Wtf8>` is safe.
550 unsafe { mem::transmute(boxed) }
551 }
552
553 #[rustc_allow_incoherent_impl]
554 pub fn empty_box() -> Box<Wtf8> {
555 let boxed: Box<[u8]> = Default::default();
556 // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`, and `boxed` is
557 // an empty `Box<[u8]>`. Therefore, transmuting it to `Box<Wtf8>` is safe.
558 unsafe { mem::transmute(boxed) }
559 }
560
561 #[cfg(target_has_atomic = "ptr")]
562 #[rustc_allow_incoherent_impl]
563 pub fn into_arc(&self) -> Arc<Wtf8> {
564 let arc: Arc<[u8]> = Arc::from(self.as_bytes());
565 // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`.
566 unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Wtf8) }
567 }
568
569 #[rustc_allow_incoherent_impl]
570 pub fn into_rc(&self) -> Rc<Wtf8> {
571 let rc: Rc<[u8]> = Rc::from(self.as_bytes());
572 // SAFETY: `Wtf8` is a transparent wrapper around `[u8]`.
573 unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Wtf8) }
574 }
575
576 #[inline]
577 #[rustc_allow_incoherent_impl]
578 pub fn to_ascii_lowercase(&self) -> Wtf8Buf {
579 Wtf8Buf { bytes: self.as_bytes().to_ascii_lowercase(), is_known_utf8: false }
580 }
581
582 #[inline]
583 #[rustc_allow_incoherent_impl]
584 pub fn to_ascii_uppercase(&self) -> Wtf8Buf {
585 Wtf8Buf { bytes: self.as_bytes().to_ascii_uppercase(), is_known_utf8: false }
586 }
587}
588
589#[inline]
590fn decode_surrogate_pair(lead: u16, trail: u16) -> char {
591 let code_point = 0x10000 + ((((lead - 0xD800) as u32) << 10) | (trail - 0xDC00) as u32);
592 // SAFETY: The computed `code_point` is in 0x10000..=0x10FFFF and is not a surrogate.
593 unsafe { char::from_u32_unchecked(code_point) }
594}
595
596impl Hash for Wtf8Buf {
597 #[inline]
598 fn hash<H: Hasher>(&self, state: &mut H) {
599 state.write(&self.bytes);
600 0xfeu8.hash(state)
601 }
602}