1#![unstable(
14 feature = "wtf8_internals",
15 issue = "none",
16 reason = "this is internal code for representing OsStr on some platforms and not a public API"
17)]
18#![doc(hidden)]
21
22use crate::char::{EscapeDebugExtArgs, encode_utf16_raw};
23use crate::clone::CloneToUninit;
24use crate::fmt::{self, Write};
25use crate::hash::{Hash, Hasher};
26use crate::iter::FusedIterator;
27use crate::num::niche_types::CodePointInner;
28use crate::str::next_code_point;
29use crate::{ops, slice, str};
30
31#[derive(#[automatically_derived]
impl crate::cmp::Eq for CodePoint {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: crate::cmp::AssertParamIsEq<CodePointInner>;
}
}Eq, #[automatically_derived]
impl crate::marker::StructuralPartialEq for CodePoint { }
#[automatically_derived]
impl crate::cmp::PartialEq for CodePoint {
#[inline]
fn eq(&self, other: &CodePoint) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl crate::cmp::Ord for CodePoint {
#[inline]
fn cmp(&self, other: &CodePoint) -> crate::cmp::Ordering {
crate::cmp::Ord::cmp(&self.0, &other.0)
}
}Ord, #[automatically_derived]
impl crate::cmp::PartialOrd for CodePoint {
#[inline]
fn partial_cmp(&self, other: &CodePoint)
-> crate::option::Option<crate::cmp::Ordering> {
crate::option::Option::Some(crate::cmp::Ord::cmp(self, other))
}
}PartialOrd, #[automatically_derived]
#[doc(hidden)]
unsafe impl crate::clone::TrivialClone for CodePoint { }
#[automatically_derived]
impl crate::clone::Clone for CodePoint {
#[inline]
fn clone(&self) -> CodePoint {
let _: crate::clone::AssertParamIsClone<CodePointInner>;
*self
}
}Clone, #[automatically_derived]
impl crate::marker::Copy for CodePoint { }Copy)]
37#[doc(hidden)]
38pub struct CodePoint(CodePointInner);
39
40impl fmt::Debug for CodePoint {
43 #[inline]
44 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
45 formatter.write_fmt(format_args!("U+{0:04X}", self.0.as_inner()))write!(formatter, "U+{:04X}", self.0.as_inner())
46 }
47}
48
49impl CodePoint {
50 #[inline]
56 pub unsafe fn from_u32_unchecked(value: u32) -> CodePoint {
57 CodePoint(unsafe { CodePointInner::new_unchecked(value) })
59 }
60
61 #[inline]
65 pub fn from_u32(value: u32) -> Option<CodePoint> {
66 Some(CodePoint(CodePointInner::new(value)?))
67 }
68
69 #[inline]
73 pub fn from_char(value: char) -> CodePoint {
74 unsafe { CodePoint::from_u32_unchecked(value as u32) }
76 }
77
78 #[inline]
80 pub fn to_u32(&self) -> u32 {
81 self.0.as_inner()
82 }
83
84 #[inline]
86 pub fn to_lead_surrogate(&self) -> Option<u16> {
87 match self.to_u32() {
88 lead @ 0xD800..=0xDBFF => Some(lead as u16),
89 _ => None,
90 }
91 }
92
93 #[inline]
95 pub fn to_trail_surrogate(&self) -> Option<u16> {
96 match self.to_u32() {
97 trail @ 0xDC00..=0xDFFF => Some(trail as u16),
98 _ => None,
99 }
100 }
101
102 #[inline]
106 pub fn to_char(&self) -> Option<char> {
107 match self.to_u32() {
108 0xD800..=0xDFFF => None,
109 valid => Some(unsafe { char::from_u32_unchecked(valid) }),
111 }
112 }
113
114 #[inline]
119 pub fn to_char_lossy(&self) -> char {
120 self.to_char().unwrap_or(char::REPLACEMENT_CHARACTER)
121 }
122}
123
124#[derive(#[automatically_derived]
impl crate::cmp::Eq for Wtf8 {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: crate::cmp::AssertParamIsEq<[u8]>;
}
}Eq, #[automatically_derived]
impl crate::cmp::Ord for Wtf8 {
#[inline]
fn cmp(&self, other: &Wtf8) -> crate::cmp::Ordering {
crate::cmp::Ord::cmp(&self.bytes, &other.bytes)
}
}Ord, #[automatically_derived]
impl crate::marker::StructuralPartialEq for Wtf8 { }
#[automatically_derived]
impl crate::cmp::PartialEq for Wtf8 {
#[inline]
fn eq(&self, other: &Wtf8) -> bool { self.bytes == other.bytes }
}PartialEq, #[automatically_derived]
impl crate::cmp::PartialOrd for Wtf8 {
#[inline]
fn partial_cmp(&self, other: &Wtf8)
-> crate::option::Option<crate::cmp::Ordering> {
crate::option::Option::Some(crate::cmp::Ord::cmp(self, other))
}
}PartialOrd)]
129#[repr(transparent)]
130#[rustc_has_incoherent_inherent_impls]
131#[doc(hidden)]
132pub struct Wtf8 {
133 bytes: [u8],
134}
135
136impl AsRef<[u8]> for Wtf8 {
137 #[inline]
138 fn as_ref(&self) -> &[u8] {
139 &self.bytes
140 }
141}
142
143impl fmt::Debug for Wtf8 {
147 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
148 fn write_str_escaped(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
149 use crate::fmt::Write as _;
150 for c in s.chars().flat_map(|c| {
151 c.escape_debug_ext(EscapeDebugExtArgs {
152 escape_single_quote: false,
153 escape_double_quote: true,
154 })
155 }) {
156 f.write_char(c)?
157 }
158 Ok(())
159 }
160
161 formatter.write_char('"')?;
162 let mut pos = 0;
163 while let Some((surrogate_pos, surrogate)) = self.next_surrogate(pos) {
164 write_str_escaped(formatter, unsafe {
166 str::from_utf8_unchecked(&self.bytes[pos..surrogate_pos])
167 })?;
168 formatter.write_fmt(format_args!("\\u{{{0:x}}}", surrogate))write!(formatter, "\\u{{{:x}}}", surrogate)?;
169 pos = surrogate_pos + 3;
170 }
171
172 write_str_escaped(formatter, unsafe { str::from_utf8_unchecked(&self.bytes[pos..]) })?;
174 formatter.write_char('"')
175 }
176}
177
178impl fmt::Display for Wtf8 {
181 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
182 let wtf8_bytes = &self.bytes;
183 let mut pos = 0;
184 loop {
185 match self.next_surrogate(pos) {
186 Some((surrogate_pos, _)) => {
187 formatter.write_str(unsafe {
189 str::from_utf8_unchecked(&wtf8_bytes[pos..surrogate_pos])
190 })?;
191 formatter.write_char(char::REPLACEMENT_CHARACTER)?;
192 pos = surrogate_pos + 3;
193 }
194 None => {
195 let s = unsafe { str::from_utf8_unchecked(&wtf8_bytes[pos..]) };
197 if pos == 0 { return s.fmt(formatter) } else { return formatter.write_str(s) }
198 }
199 }
200 }
201 }
202}
203
204impl Wtf8 {
205 #[inline]
207 pub fn from_str(value: &str) -> &Wtf8 {
208 unsafe { Wtf8::from_bytes_unchecked(value.as_bytes()) }
210 }
211
212 #[inline]
218 pub unsafe fn from_bytes_unchecked(value: &[u8]) -> &Wtf8 {
219 unsafe { &*(value as *const [u8] as *const Wtf8) }
221 }
222
223 #[inline]
229 pub unsafe fn from_mut_bytes_unchecked(value: &mut [u8]) -> &mut Wtf8 {
230 unsafe { &mut *(value as *mut [u8] as *mut Wtf8) }
232 }
233
234 #[inline]
236 pub fn len(&self) -> usize {
237 self.bytes.len()
238 }
239
240 #[inline]
241 pub fn is_empty(&self) -> bool {
242 self.bytes.is_empty()
243 }
244
245 #[inline]
252 pub fn ascii_byte_at(&self, position: usize) -> u8 {
253 match self.bytes[position] {
254 ascii_byte @ 0x00..=0x7F => ascii_byte,
255 _ => 0xFF,
256 }
257 }
258
259 #[inline]
261 pub fn code_points(&self) -> Wtf8CodePoints<'_> {
262 Wtf8CodePoints { bytes: self.bytes.iter() }
263 }
264
265 #[inline]
267 pub fn as_bytes(&self) -> &[u8] {
268 &self.bytes
269 }
270
271 #[inline]
277 pub fn as_str(&self) -> Result<&str, str::Utf8Error> {
278 str::from_utf8(&self.bytes)
279 }
280
281 #[inline]
288 pub fn encode_wide(&self) -> EncodeWide<'_> {
289 EncodeWide { code_points: self.code_points(), extra: 0 }
290 }
291
292 #[inline]
293 pub fn next_surrogate(&self, mut pos: usize) -> Option<(usize, u16)> {
294 let mut iter = self.bytes[pos..].iter();
295 loop {
296 let b = *iter.next()?;
297 if b < 0x80 {
298 pos += 1;
299 } else if b < 0xE0 {
300 iter.next();
301 pos += 2;
302 } else if b == 0xED {
303 match (iter.next(), iter.next()) {
304 (Some(&b2), Some(&b3)) if b2 >= 0xA0 => {
305 return Some((pos, decode_surrogate(b2, b3)));
306 }
307 _ => pos += 3,
308 }
309 } else if b < 0xF0 {
310 iter.next();
311 iter.next();
312 pos += 3;
313 } else {
314 iter.next();
315 iter.next();
316 iter.next();
317 pos += 4;
318 }
319 }
320 }
321
322 #[inline]
323 pub fn final_lead_surrogate(&self) -> Option<u16> {
324 match self.bytes {
325 [.., 0xED, b2 @ 0xA0..=0xAF, b3] => Some(decode_surrogate(b2, b3)),
326 _ => None,
327 }
328 }
329
330 #[inline]
331 pub fn initial_trail_surrogate(&self) -> Option<u16> {
332 match self.bytes {
333 [0xED, b2 @ 0xB0..=0xBF, b3, ..] => Some(decode_surrogate(b2, b3)),
334 _ => None,
335 }
336 }
337
338 #[inline]
339 pub fn make_ascii_lowercase(&mut self) {
340 self.bytes.make_ascii_lowercase()
341 }
342
343 #[inline]
344 pub fn make_ascii_uppercase(&mut self) {
345 self.bytes.make_ascii_uppercase()
346 }
347
348 #[inline]
349 pub fn is_ascii(&self) -> bool {
350 self.bytes.is_ascii()
351 }
352
353 #[inline]
354 pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
355 self.bytes.eq_ignore_ascii_case(&other.bytes)
356 }
357}
358
359impl ops::Index<ops::Range<usize>> for Wtf8 {
366 type Output = Wtf8;
367
368 #[inline]
369 fn index(&self, range: ops::Range<usize>) -> &Wtf8 {
370 if range.start <= range.end
371 && self.is_code_point_boundary(range.start)
372 && self.is_code_point_boundary(range.end)
373 {
374 unsafe { slice_unchecked(self, range.start, range.end) }
376 } else {
377 slice_error_fail(self, range.start, range.end)
378 }
379 }
380}
381
382impl ops::Index<ops::RangeFrom<usize>> for Wtf8 {
389 type Output = Wtf8;
390
391 #[inline]
392 fn index(&self, range: ops::RangeFrom<usize>) -> &Wtf8 {
393 if self.is_code_point_boundary(range.start) {
394 unsafe { slice_unchecked(self, range.start, self.len()) }
396 } else {
397 slice_error_fail(self, range.start, self.len())
398 }
399 }
400}
401
402impl ops::Index<ops::RangeTo<usize>> for Wtf8 {
409 type Output = Wtf8;
410
411 #[inline]
412 fn index(&self, range: ops::RangeTo<usize>) -> &Wtf8 {
413 if self.is_code_point_boundary(range.end) {
414 unsafe { slice_unchecked(self, 0, range.end) }
416 } else {
417 slice_error_fail(self, 0, range.end)
418 }
419 }
420}
421
422impl ops::Index<ops::RangeFull> for Wtf8 {
423 type Output = Wtf8;
424
425 #[inline]
426 fn index(&self, _range: ops::RangeFull) -> &Wtf8 {
427 self
428 }
429}
430
431#[inline]
432fn decode_surrogate(second_byte: u8, third_byte: u8) -> u16 {
433 0xD800 | (second_byte as u16 & 0x3F) << 6 | third_byte as u16 & 0x3F
435}
436
437impl Wtf8 {
438 #[inline]
440 pub fn is_code_point_boundary(&self, index: usize) -> bool {
441 if index == 0 {
442 return true;
443 }
444 match self.bytes.get(index) {
445 None => index == self.len(),
446 Some(&b) => (b as i8) >= -0x40,
447 }
448 }
449
450 #[track_caller]
458 #[inline]
459 pub fn check_utf8_boundary(&self, index: usize) {
460 let Err(err) = self.try_check_utf8_boundary(index) else { return };
461 match err {
462 Utf8BoundaryError::NotABoundary => {
463 {
crate::panicking::panic_fmt(format_args!("byte index {0} is not a codepoint boundary",
index));
}panic!("byte index {index} is not a codepoint boundary")
464 }
465 Utf8BoundaryError::OutOfBounds => {
crate::panicking::panic_fmt(format_args!("byte index {0} is out of bounds",
index));
}panic!("byte index {index} is out of bounds"),
466 Utf8BoundaryError::BetweenSurrogates => {
467 {
crate::panicking::panic_fmt(format_args!("byte index {0} lies between surrogate codepoints",
index));
}panic!("byte index {index} lies between surrogate codepoints")
468 }
469 }
470 }
471
472 #[track_caller]
473 #[inline]
474 pub fn try_check_utf8_boundary(&self, index: usize) -> Result<(), Utf8BoundaryError> {
475 if index == 0 {
476 return Ok(());
477 }
478 match self.bytes.get(index) {
479 Some(0xED) => (), Some(&b) if (b as i8) >= -0x40 => return Ok(()),
481 Some(_) => return Err(Utf8BoundaryError::NotABoundary),
482 None if index == self.len() => return Ok(()),
483 None => return Err(Utf8BoundaryError::OutOfBounds),
484 }
485 if self.bytes[index + 1] >= 0xA0 {
486 if index >= 3 && self.bytes[index - 3] == 0xED && self.bytes[index - 2] >= 0xA0 {
488 return Err(Utf8BoundaryError::BetweenSurrogates);
489 }
490 }
491 Ok(())
492 }
493}
494
495#[derive(#[automatically_derived]
impl crate::fmt::Debug for Utf8BoundaryError {
#[inline]
fn fmt(&self, f: &mut crate::fmt::Formatter) -> crate::fmt::Result {
crate::fmt::Formatter::write_str(f,
match self {
Utf8BoundaryError::NotABoundary => "NotABoundary",
Utf8BoundaryError::OutOfBounds => "OutOfBounds",
Utf8BoundaryError::BetweenSurrogates => "BetweenSurrogates",
})
}
}Debug)]
498pub enum Utf8BoundaryError {
499 NotABoundary,
500 OutOfBounds,
501 BetweenSurrogates,
502}
503
504#[inline]
506unsafe fn slice_unchecked(s: &Wtf8, begin: usize, end: usize) -> &Wtf8 {
507 unsafe {
509 let len = end - begin;
510 let start = s.as_bytes().as_ptr().add(begin);
511 Wtf8::from_bytes_unchecked(slice::from_raw_parts(start, len))
512 }
513}
514
515#[inline(never)]
516fn slice_error_fail(s: &Wtf8, begin: usize, end: usize) -> ! {
517 let len = s.len();
518 if begin > len {
519 {
crate::panicking::panic_fmt(format_args!("start byte index {0} is out of bounds for string of length {1}",
begin, len));
};panic!("start byte index {begin} is out of bounds for string of length {len}");
520 }
521 if end > len {
522 {
crate::panicking::panic_fmt(format_args!("end byte index {0} is out of bounds for string of length {1}",
end, len));
};panic!("end byte index {end} is out of bounds for string of length {len}");
523 }
524 if begin > end {
525 {
crate::panicking::panic_fmt(format_args!("byte range starts at {0} but ends at {1}",
begin, end));
};panic!("byte range starts at {begin} but ends at {end}");
526 }
527 if !s.is_code_point_boundary(begin) {
528 {
crate::panicking::panic_fmt(format_args!("byte index {0} is not a code point boundary",
begin));
};panic!("byte index {begin} is not a code point boundary");
529 }
530 {
crate::panicking::panic_fmt(format_args!("byte index {0} is not a code point boundary",
end));
};panic!("byte index {end} is not a code point boundary");
531}
532
533#[derive(#[automatically_derived]
impl<'a> crate::clone::Clone for Wtf8CodePoints<'a> {
#[inline]
fn clone(&self) -> Wtf8CodePoints<'a> {
Wtf8CodePoints { bytes: crate::clone::Clone::clone(&self.bytes) }
}
}Clone)]
537#[doc(hidden)]
538pub struct Wtf8CodePoints<'a> {
539 bytes: slice::Iter<'a, u8>,
540}
541
542impl Iterator for Wtf8CodePoints<'_> {
543 type Item = CodePoint;
544
545 #[inline]
546 fn next(&mut self) -> Option<CodePoint> {
547 unsafe { next_code_point(&mut self.bytes).map(|c| CodePoint::from_u32_unchecked(c)) }
549 }
550
551 #[inline]
552 fn size_hint(&self) -> (usize, Option<usize>) {
553 let len = self.bytes.len();
554 (len.saturating_add(3) / 4, Some(len))
555 }
556}
557
558impl fmt::Debug for Wtf8CodePoints<'_> {
559 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
560 f.debug_tuple("Wtf8CodePoints")
561 .field(&unsafe { Wtf8::from_bytes_unchecked(self.bytes.as_slice()) })
563 .finish()
564 }
565}
566
567#[stable(feature = "rust1", since = "1.0.0")]
569#[derive(#[automatically_derived]
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> crate::clone::Clone for EncodeWide<'a> {
#[inline]
fn clone(&self) -> EncodeWide<'a> {
EncodeWide {
code_points: crate::clone::Clone::clone(&self.code_points),
extra: crate::clone::Clone::clone(&self.extra),
}
}
}Clone)]
570#[doc(hidden)]
571pub struct EncodeWide<'a> {
572 code_points: Wtf8CodePoints<'a>,
573 extra: u16,
574}
575
576#[stable(feature = "rust1", since = "1.0.0")]
578impl Iterator for EncodeWide<'_> {
579 type Item = u16;
580
581 #[inline]
582 fn next(&mut self) -> Option<u16> {
583 if self.extra != 0 {
584 let tmp = self.extra;
585 self.extra = 0;
586 return Some(tmp);
587 }
588
589 let mut buf = [0; char::MAX_LEN_UTF16];
590 self.code_points.next().map(|code_point| {
591 let n = encode_utf16_raw(code_point.to_u32(), &mut buf).len();
592 if n == 2 {
593 self.extra = buf[1];
594 }
595 buf[0]
596 })
597 }
598
599 #[inline]
600 fn size_hint(&self) -> (usize, Option<usize>) {
601 let (low, high) = self.code_points.size_hint();
602 let ext = (self.extra != 0) as usize;
603 (low + ext, high.and_then(|n| n.checked_mul(2)).and_then(|n| n.checked_add(ext)))
607 }
608}
609
610#[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")]
611impl FusedIterator for EncodeWide<'_> {}
612
613#[stable(feature = "encode_wide_debug", since = "1.92.0")]
614impl fmt::Debug for EncodeWide<'_> {
615 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
616 struct CodeUnit(u16);
617 impl fmt::Debug for CodeUnit {
618 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
619 match char::from_u32(self.0 as u32) {
627 Some(c) => f.write_fmt(format_args!("{0:?}", c))write!(f, "{c:?}"),
628 None => f.write_fmt(format_args!("0x{0:04X}", self.0))write!(f, "0x{:04X}", self.0),
629 }
630 }
631 }
632
633 f.write_fmt(format_args!("EncodeWide("))write!(f, "EncodeWide(")?;
634 f.debug_list().entries(self.clone().map(CodeUnit)).finish()?;
635 f.write_fmt(format_args!(")"))write!(f, ")")?;
636 Ok(())
637 }
638}
639
640impl Hash for CodePoint {
641 #[inline]
642 fn hash<H: Hasher>(&self, state: &mut H) {
643 self.0.hash(state)
644 }
645}
646
647impl Hash for Wtf8 {
648 #[inline]
649 fn hash<H: Hasher>(&self, state: &mut H) {
650 state.write(&self.bytes);
651 0xfeu8.hash(state)
652 }
653}
654
655#[unstable(feature = "clone_to_uninit", issue = "126799")]
656unsafe impl CloneToUninit for Wtf8 {
657 #[inline]
658 #[cfg_attr(debug_assertions, track_caller)]
659 unsafe fn clone_to_uninit(&self, dst: *mut u8) {
660 unsafe { self.bytes.clone_to_uninit(dst) }
662 }
663}