bstr/utf8.rs
1use core::{char, cmp, fmt, str};
2
3use crate::{ascii, bstr::BStr, ext_slice::ByteSlice};
4
5// The UTF-8 decoder provided here is based on the one presented here:
6// https://bjoern.hoehrmann.de/utf-8/decoder/dfa/
7//
8// We *could* have done UTF-8 decoding by using a DFA generated by `\p{any}`
9// using regex-automata that is roughly the same size. The real benefit of
10// Hoehrmann's formulation is that the byte class mapping below is manually
11// tailored such that each byte's class doubles as a shift to mask out the
12// bits necessary for constructing the leading bits of each codepoint value
13// from the initial byte.
14//
15// There are some minor differences between this implementation and Hoehrmann's
16// formulation.
17//
18// Firstly, we make REJECT have state ID 0, since it makes the state table
19// itself a little easier to read and is consistent with the notion that 0
20// means "false" or "bad."
21//
22// Secondly, when doing bulk decoding, we add a SIMD accelerated ASCII fast
23// path.
24//
25// Thirdly, we pre-multiply the state IDs to avoid a multiplication instruction
26// in the core decoding loop. (Which is what regex-automata would do by
27// default.)
28//
29// Fourthly, we split the byte class mapping and transition table into two
30// arrays because it's clearer.
31//
32// It is unlikely that this is the fastest way to do UTF-8 decoding, however,
33// it is fairly simple.
34
35const ACCEPT: usize = 12;
36const REJECT: usize = 0;
37
38/// SAFETY: The decode below function relies on the correctness of these
39/// equivalence classes.
40#[cfg_attr(rustfmt, rustfmt::skip)]
41const CLASSES: [u8; 256] = [
42 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
43 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
44 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
45 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
46 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
47 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
48 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
49 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
50];
51
52/// SAFETY: The decode below function relies on the correctness of this state
53/// machine.
54#[cfg_attr(rustfmt, rustfmt::skip)]
55const STATES_FORWARD: &'static [u8] = &[
56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57 12, 0, 24, 36, 60, 96, 84, 0, 0, 0, 48, 72,
58 0, 12, 0, 0, 0, 0, 0, 12, 0, 12, 0, 0,
59 0, 24, 0, 0, 0, 0, 0, 24, 0, 24, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0,
61 0, 24, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 36, 0, 36, 0, 0,
63 0, 36, 0, 0, 0, 0, 0, 36, 0, 36, 0, 0,
64 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65];
66
67/// An iterator over Unicode scalar values in a byte string.
68///
69/// When invalid UTF-8 byte sequences are found, they are substituted with the
70/// Unicode replacement codepoint (`U+FFFD`) using the
71/// ["maximal subpart" strategy](https://www.unicode.org/review/pr-121.html).
72///
73/// This iterator is created by the
74/// [`chars`](trait.ByteSlice.html#method.chars) method provided by the
75/// [`ByteSlice`](trait.ByteSlice.html) extension trait for `&[u8]`.
76#[derive(Clone, Debug)]
77pub struct Chars<'a> {
78 bs: &'a [u8],
79}
80
81impl<'a> Chars<'a> {
82 pub(crate) fn new(bs: &'a [u8]) -> Chars<'a> {
83 Chars { bs }
84 }
85
86 /// View the underlying data as a subslice of the original data.
87 ///
88 /// The slice returned has the same lifetime as the original slice, and so
89 /// the iterator can continue to be used while this exists.
90 ///
91 /// # Examples
92 ///
93 /// ```
94 /// use bstr::ByteSlice;
95 ///
96 /// let mut chars = b"abc".chars();
97 ///
98 /// assert_eq!(b"abc", chars.as_bytes());
99 /// chars.next();
100 /// assert_eq!(b"bc", chars.as_bytes());
101 /// chars.next();
102 /// chars.next();
103 /// assert_eq!(b"", chars.as_bytes());
104 /// ```
105 #[inline]
106 pub fn as_bytes(&self) -> &'a [u8] {
107 self.bs
108 }
109}
110
111impl<'a> Iterator for Chars<'a> {
112 type Item = char;
113
114 #[inline]
115 fn next(&mut self) -> Option<char> {
116 let (ch, size) = decode_lossy(self.bs);
117 if size == 0 {
118 return None;
119 }
120 self.bs = &self.bs[size..];
121 Some(ch)
122 }
123}
124
125impl<'a> DoubleEndedIterator for Chars<'a> {
126 #[inline]
127 fn next_back(&mut self) -> Option<char> {
128 let (ch, size) = decode_last_lossy(self.bs);
129 if size == 0 {
130 return None;
131 }
132 self.bs = &self.bs[..self.bs.len() - size];
133 Some(ch)
134 }
135}
136
137/// An iterator over Unicode scalar values in a byte string and their
138/// byte index positions.
139///
140/// When invalid UTF-8 byte sequences are found, they are substituted with the
141/// Unicode replacement codepoint (`U+FFFD`) using the
142/// ["maximal subpart" strategy](https://www.unicode.org/review/pr-121.html).
143///
144/// Note that this is slightly different from the `CharIndices` iterator
145/// provided by the standard library. Aside from working on possibly invalid
146/// UTF-8, this iterator provides both the corresponding starting and ending
147/// byte indices of each codepoint yielded. The ending position is necessary to
148/// slice the original byte string when invalid UTF-8 bytes are converted into
149/// a Unicode replacement codepoint, since a single replacement codepoint can
150/// substitute anywhere from 1 to 3 invalid bytes (inclusive).
151///
152/// This iterator is created by the
153/// [`char_indices`](trait.ByteSlice.html#method.char_indices) method provided
154/// by the [`ByteSlice`](trait.ByteSlice.html) extension trait for `&[u8]`.
155#[derive(Clone, Debug)]
156pub struct CharIndices<'a> {
157 bs: &'a [u8],
158 forward_index: usize,
159 reverse_index: usize,
160}
161
162impl<'a> CharIndices<'a> {
163 pub(crate) fn new(bs: &'a [u8]) -> CharIndices<'a> {
164 CharIndices { bs, forward_index: 0, reverse_index: bs.len() }
165 }
166
167 /// View the underlying data as a subslice of the original data.
168 ///
169 /// The slice returned has the same lifetime as the original slice, and so
170 /// the iterator can continue to be used while this exists.
171 ///
172 /// # Examples
173 ///
174 /// ```
175 /// use bstr::ByteSlice;
176 ///
177 /// let mut it = b"abc".char_indices();
178 ///
179 /// assert_eq!(b"abc", it.as_bytes());
180 /// it.next();
181 /// assert_eq!(b"bc", it.as_bytes());
182 /// it.next();
183 /// it.next();
184 /// assert_eq!(b"", it.as_bytes());
185 /// ```
186 #[inline]
187 pub fn as_bytes(&self) -> &'a [u8] {
188 self.bs
189 }
190}
191
192impl<'a> Iterator for CharIndices<'a> {
193 type Item = (usize, usize, char);
194
195 #[inline]
196 fn next(&mut self) -> Option<(usize, usize, char)> {
197 let index = self.forward_index;
198 let (ch, size) = decode_lossy(self.bs);
199 if size == 0 {
200 return None;
201 }
202 self.bs = &self.bs[size..];
203 self.forward_index += size;
204 Some((index, index + size, ch))
205 }
206}
207
208impl<'a> DoubleEndedIterator for CharIndices<'a> {
209 #[inline]
210 fn next_back(&mut self) -> Option<(usize, usize, char)> {
211 let (ch, size) = decode_last_lossy(self.bs);
212 if size == 0 {
213 return None;
214 }
215 self.bs = &self.bs[..self.bs.len() - size];
216 self.reverse_index -= size;
217 Some((self.reverse_index, self.reverse_index + size, ch))
218 }
219}
220
221impl<'a> ::core::iter::FusedIterator for CharIndices<'a> {}
222
223/// An iterator over chunks of valid UTF-8 in a byte slice.
224///
225/// See [`utf8_chunks`](trait.ByteSlice.html#method.utf8_chunks).
226#[derive(Clone, Debug)]
227pub struct Utf8Chunks<'a> {
228 pub(super) bytes: &'a [u8],
229}
230
231/// A chunk of valid UTF-8, possibly followed by invalid UTF-8 bytes.
232///
233/// This is yielded by the
234/// [`Utf8Chunks`](struct.Utf8Chunks.html)
235/// iterator, which can be created via the
236/// [`ByteSlice::utf8_chunks`](trait.ByteSlice.html#method.utf8_chunks)
237/// method.
238///
239/// The `'a` lifetime parameter corresponds to the lifetime of the bytes that
240/// are being iterated over.
241#[cfg_attr(test, derive(Debug, PartialEq))]
242pub struct Utf8Chunk<'a> {
243 /// A valid UTF-8 piece, at the start, end, or between invalid UTF-8 bytes.
244 ///
245 /// This is empty between adjacent invalid UTF-8 byte sequences.
246 valid: &'a str,
247 /// A sequence of invalid UTF-8 bytes.
248 ///
249 /// Can only be empty in the last chunk.
250 ///
251 /// Should be replaced by a single unicode replacement character, if not
252 /// empty.
253 invalid: &'a BStr,
254 /// Indicates whether the invalid sequence could've been valid if there
255 /// were more bytes.
256 ///
257 /// Can only be true in the last chunk.
258 incomplete: bool,
259}
260
261impl<'a> Utf8Chunk<'a> {
262 /// Returns the (possibly empty) valid UTF-8 bytes in this chunk.
263 ///
264 /// This may be empty if there are consecutive sequences of invalid UTF-8
265 /// bytes.
266 #[inline]
267 pub fn valid(&self) -> &'a str {
268 self.valid
269 }
270
271 /// Returns the (possibly empty) invalid UTF-8 bytes in this chunk that
272 /// immediately follow the valid UTF-8 bytes in this chunk.
273 ///
274 /// This is only empty when this chunk corresponds to the last chunk in
275 /// the original bytes.
276 ///
277 /// The maximum length of this slice is 3. That is, invalid UTF-8 byte
278 /// sequences greater than 1 always correspond to a valid _prefix_ of
279 /// a valid UTF-8 encoded codepoint. This corresponds to the "substitution
280 /// of maximal subparts" strategy that is described in more detail in the
281 /// docs for the
282 /// [`ByteSlice::to_str_lossy`](trait.ByteSlice.html#method.to_str_lossy)
283 /// method.
284 #[inline]
285 pub fn invalid(&self) -> &'a [u8] {
286 self.invalid.as_bytes()
287 }
288
289 /// Returns whether the invalid sequence might still become valid if more
290 /// bytes are added.
291 ///
292 /// Returns true if the end of the input was reached unexpectedly,
293 /// without encountering an unexpected byte.
294 ///
295 /// This can only be the case for the last chunk.
296 #[inline]
297 pub fn incomplete(&self) -> bool {
298 self.incomplete
299 }
300}
301
302impl<'a> Iterator for Utf8Chunks<'a> {
303 type Item = Utf8Chunk<'a>;
304
305 #[inline]
306 fn next(&mut self) -> Option<Utf8Chunk<'a>> {
307 if self.bytes.is_empty() {
308 return None;
309 }
310 match validate(self.bytes) {
311 Ok(()) => {
312 let valid = self.bytes;
313 self.bytes = &[];
314 Some(Utf8Chunk {
315 // SAFETY: This is safe because of the guarantees provided
316 // by utf8::validate.
317 valid: unsafe { str::from_utf8_unchecked(valid) },
318 invalid: [].as_bstr(),
319 incomplete: false,
320 })
321 }
322 Err(e) => {
323 let (valid, rest) = self.bytes.split_at(e.valid_up_to());
324 // SAFETY: This is safe because of the guarantees provided by
325 // utf8::validate.
326 let valid = unsafe { str::from_utf8_unchecked(valid) };
327 let (invalid_len, incomplete) = match e.error_len() {
328 Some(n) => (n, false),
329 None => (rest.len(), true),
330 };
331 let (invalid, rest) = rest.split_at(invalid_len);
332 self.bytes = rest;
333 Some(Utf8Chunk {
334 valid,
335 invalid: invalid.as_bstr(),
336 incomplete,
337 })
338 }
339 }
340 }
341
342 #[inline]
343 fn size_hint(&self) -> (usize, Option<usize>) {
344 if self.bytes.is_empty() {
345 (0, Some(0))
346 } else {
347 (1, Some(self.bytes.len()))
348 }
349 }
350}
351
352impl<'a> ::core::iter::FusedIterator for Utf8Chunks<'a> {}
353
354/// An error that occurs when UTF-8 decoding fails.
355///
356/// This error occurs when attempting to convert a non-UTF-8 byte
357/// string to a Rust string that must be valid UTF-8. For example,
358/// [`to_str`](trait.ByteSlice.html#method.to_str) is one such method.
359///
360/// # Example
361///
362/// This example shows what happens when a given byte sequence is invalid,
363/// but ends with a sequence that is a possible prefix of valid UTF-8.
364///
365/// ```
366/// use bstr::{B, ByteSlice};
367///
368/// let s = B(b"foobar\xF1\x80\x80");
369/// let err = s.to_str().unwrap_err();
370/// assert_eq!(err.valid_up_to(), 6);
371/// assert_eq!(err.error_len(), None);
372/// ```
373///
374/// This example shows what happens when a given byte sequence contains
375/// invalid UTF-8.
376///
377/// ```
378/// use bstr::ByteSlice;
379///
380/// let s = b"foobar\xF1\x80\x80quux";
381/// let err = s.to_str().unwrap_err();
382/// assert_eq!(err.valid_up_to(), 6);
383/// // The error length reports the maximum number of bytes that correspond to
384/// // a valid prefix of a UTF-8 encoded codepoint.
385/// assert_eq!(err.error_len(), Some(3));
386///
387/// // In contrast to the above which contains a single invalid prefix,
388/// // consider the case of multiple individual bytes that are never valid
389/// // prefixes. Note how the value of error_len changes!
390/// let s = b"foobar\xFF\xFFquux";
391/// let err = s.to_str().unwrap_err();
392/// assert_eq!(err.valid_up_to(), 6);
393/// assert_eq!(err.error_len(), Some(1));
394///
395/// // The fact that it's an invalid prefix does not change error_len even
396/// // when it immediately precedes the end of the string.
397/// let s = b"foobar\xFF";
398/// let err = s.to_str().unwrap_err();
399/// assert_eq!(err.valid_up_to(), 6);
400/// assert_eq!(err.error_len(), Some(1));
401/// ```
402#[derive(Clone, Debug, Eq, PartialEq)]
403pub struct Utf8Error {
404 valid_up_to: usize,
405 error_len: Option<usize>,
406}
407
408impl Utf8Error {
409 /// Returns the byte index of the position immediately following the last
410 /// valid UTF-8 byte.
411 ///
412 /// # Example
413 ///
414 /// This examples shows how `valid_up_to` can be used to retrieve a
415 /// possibly empty prefix that is guaranteed to be valid UTF-8:
416 ///
417 /// ```
418 /// use bstr::ByteSlice;
419 ///
420 /// let s = b"foobar\xF1\x80\x80quux";
421 /// let err = s.to_str().unwrap_err();
422 ///
423 /// // This is guaranteed to never panic.
424 /// let string = s[..err.valid_up_to()].to_str().unwrap();
425 /// assert_eq!(string, "foobar");
426 /// ```
427 #[inline]
428 pub fn valid_up_to(&self) -> usize {
429 self.valid_up_to
430 }
431
432 /// Returns the total number of invalid UTF-8 bytes immediately following
433 /// the position returned by `valid_up_to`. This value is always at least
434 /// `1`, but can be up to `3` if bytes form a valid prefix of some UTF-8
435 /// encoded codepoint.
436 ///
437 /// If the end of the original input was found before a valid UTF-8 encoded
438 /// codepoint could be completed, then this returns `None`. This is useful
439 /// when processing streams, where a `None` value signals that more input
440 /// might be needed.
441 #[inline]
442 pub fn error_len(&self) -> Option<usize> {
443 self.error_len
444 }
445}
446
447#[cfg(feature = "std")]
448impl std::error::Error for Utf8Error {
449 fn description(&self) -> &str {
450 "invalid UTF-8"
451 }
452}
453
454impl fmt::Display for Utf8Error {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 write!(f, "invalid UTF-8 found at byte offset {}", self.valid_up_to)
457 }
458}
459
460/// Returns OK if and only if the given slice is completely valid UTF-8.
461///
462/// If the slice isn't valid UTF-8, then an error is returned that explains
463/// the first location at which invalid UTF-8 was detected.
464pub fn validate(slice: &[u8]) -> Result<(), Utf8Error> {
465 // The fast path for validating UTF-8. It steps through a UTF-8 automaton
466 // and uses a SIMD accelerated ASCII fast path on x86_64. If an error is
467 // detected, it backs up and runs the slower version of the UTF-8 automaton
468 // to determine correct error information.
469 fn fast(slice: &[u8]) -> Result<(), Utf8Error> {
470 let mut state = ACCEPT;
471 let mut i = 0;
472
473 while i < slice.len() {
474 let b = slice[i];
475
476 // ASCII fast path. If we see two consecutive ASCII bytes, then try
477 // to validate as much ASCII as possible very quickly.
478 if state == ACCEPT
479 && b <= 0x7F
480 && slice.get(i + 1).map_or(false, |&b| b <= 0x7F)
481 {
482 i += ascii::first_non_ascii_byte(&slice[i..]);
483 continue;
484 }
485
486 state = step(state, b);
487 if state == REJECT {
488 return Err(find_valid_up_to(slice, i));
489 }
490 i += 1;
491 }
492 if state != ACCEPT {
493 Err(find_valid_up_to(slice, slice.len()))
494 } else {
495 Ok(())
496 }
497 }
498
499 // Given the first position at which a UTF-8 sequence was determined to be
500 // invalid, return an error that correctly reports the position at which
501 // the last complete UTF-8 sequence ends.
502 #[inline(never)]
503 fn find_valid_up_to(slice: &[u8], rejected_at: usize) -> Utf8Error {
504 // In order to find the last valid byte, we need to back up an amount
505 // that guarantees every preceding byte is part of a valid UTF-8
506 // code unit sequence. To do this, we simply locate the last leading
507 // byte that occurs before rejected_at.
508 let mut backup = rejected_at.saturating_sub(1);
509 while backup > 0 && !is_leading_or_invalid_utf8_byte(slice[backup]) {
510 backup -= 1;
511 }
512 let upto = cmp::min(slice.len(), rejected_at.saturating_add(1));
513 let mut err = slow(&slice[backup..upto]).unwrap_err();
514 err.valid_up_to += backup;
515 err
516 }
517
518 // Like top-level UTF-8 decoding, except it correctly reports a UTF-8 error
519 // when an invalid sequence is found. This is split out from validate so
520 // that the fast path doesn't need to keep track of the position of the
521 // last valid UTF-8 byte. In particular, tracking this requires checking
522 // for an ACCEPT state on each byte, which degrades throughput pretty
523 // badly.
524 fn slow(slice: &[u8]) -> Result<(), Utf8Error> {
525 let mut state = ACCEPT;
526 let mut valid_up_to = 0;
527 for (i, &b) in slice.iter().enumerate() {
528 state = step(state, b);
529 if state == ACCEPT {
530 valid_up_to = i + 1;
531 } else if state == REJECT {
532 // Our error length must always be at least 1.
533 let error_len = Some(cmp::max(1, i - valid_up_to));
534 return Err(Utf8Error { valid_up_to, error_len });
535 }
536 }
537 if state != ACCEPT {
538 Err(Utf8Error { valid_up_to, error_len: None })
539 } else {
540 Ok(())
541 }
542 }
543
544 // Advance to the next state given the current state and current byte.
545 fn step(state: usize, b: u8) -> usize {
546 let class = CLASSES[b as usize];
547 // SAFETY: This is safe because 'class' is always <=11 and 'state' is
548 // always <=96. Therefore, the maximal index is 96+11 = 107, where
549 // STATES_FORWARD.len() = 108 such that every index is guaranteed to be
550 // valid by construction of the state machine and the byte equivalence
551 // classes.
552 unsafe {
553 *STATES_FORWARD.get_unchecked(state + class as usize) as usize
554 }
555 }
556
557 fast(slice)
558}
559
560/// UTF-8 decode a single Unicode scalar value from the beginning of a slice.
561///
562/// When successful, the corresponding Unicode scalar value is returned along
563/// with the number of bytes it was encoded with. The number of bytes consumed
564/// for a successful decode is always between 1 and 4, inclusive.
565///
566/// When unsuccessful, `None` is returned along with the number of bytes that
567/// make up a maximal prefix of a valid UTF-8 code unit sequence. In this case,
568/// the number of bytes consumed is always between 0 and 3, inclusive, where
569/// 0 is only returned when `slice` is empty.
570///
571/// # Examples
572///
573/// Basic usage:
574///
575/// ```
576/// use bstr::decode_utf8;
577///
578/// // Decoding a valid codepoint.
579/// let (ch, size) = decode_utf8(b"\xE2\x98\x83");
580/// assert_eq!(Some('☃'), ch);
581/// assert_eq!(3, size);
582///
583/// // Decoding an incomplete codepoint.
584/// let (ch, size) = decode_utf8(b"\xE2\x98");
585/// assert_eq!(None, ch);
586/// assert_eq!(2, size);
587/// ```
588///
589/// This example shows how to iterate over all codepoints in UTF-8 encoded
590/// bytes, while replacing invalid UTF-8 sequences with the replacement
591/// codepoint:
592///
593/// ```
594/// use bstr::{B, decode_utf8};
595///
596/// let mut bytes = B(b"\xE2\x98\x83\xFF\xF0\x9D\x9E\x83\xE2\x98\x61");
597/// let mut chars = vec![];
598/// while !bytes.is_empty() {
599/// let (ch, size) = decode_utf8(bytes);
600/// bytes = &bytes[size..];
601/// chars.push(ch.unwrap_or('\u{FFFD}'));
602/// }
603/// assert_eq!(vec!['☃', '\u{FFFD}', '𝞃', '\u{FFFD}', 'a'], chars);
604/// ```
605#[inline]
606pub fn decode<B: AsRef<[u8]>>(slice: B) -> (Option<char>, usize) {
607 let slice = slice.as_ref();
608 match slice.get(0) {
609 None => return (None, 0),
610 Some(&b) if b <= 0x7F => return (Some(b as char), 1),
611 _ => {}
612 }
613
614 let (mut state, mut cp, mut i) = (ACCEPT, 0, 0);
615 while i < slice.len() {
616 decode_step(&mut state, &mut cp, slice[i]);
617 i += 1;
618
619 if state == ACCEPT {
620 // SAFETY: This is safe because `decode_step` guarantees that
621 // `cp` is a valid Unicode scalar value in an ACCEPT state.
622 let ch = unsafe { char::from_u32_unchecked(cp) };
623 return (Some(ch), i);
624 } else if state == REJECT {
625 // At this point, we always want to advance at least one byte.
626 return (None, cmp::max(1, i.saturating_sub(1)));
627 }
628 }
629 (None, i)
630}
631
632/// Lossily UTF-8 decode a single Unicode scalar value from the beginning of a
633/// slice.
634///
635/// When successful, the corresponding Unicode scalar value is returned along
636/// with the number of bytes it was encoded with. The number of bytes consumed
637/// for a successful decode is always between 1 and 4, inclusive.
638///
639/// When unsuccessful, the Unicode replacement codepoint (`U+FFFD`) is returned
640/// along with the number of bytes that make up a maximal prefix of a valid
641/// UTF-8 code unit sequence. In this case, the number of bytes consumed is
642/// always between 0 and 3, inclusive, where 0 is only returned when `slice` is
643/// empty.
644///
645/// # Examples
646///
647/// Basic usage:
648///
649/// ```ignore
650/// use bstr::decode_utf8_lossy;
651///
652/// // Decoding a valid codepoint.
653/// let (ch, size) = decode_utf8_lossy(b"\xE2\x98\x83");
654/// assert_eq!('☃', ch);
655/// assert_eq!(3, size);
656///
657/// // Decoding an incomplete codepoint.
658/// let (ch, size) = decode_utf8_lossy(b"\xE2\x98");
659/// assert_eq!('\u{FFFD}', ch);
660/// assert_eq!(2, size);
661/// ```
662///
663/// This example shows how to iterate over all codepoints in UTF-8 encoded
664/// bytes, while replacing invalid UTF-8 sequences with the replacement
665/// codepoint:
666///
667/// ```ignore
668/// use bstr::{B, decode_utf8_lossy};
669///
670/// let mut bytes = B(b"\xE2\x98\x83\xFF\xF0\x9D\x9E\x83\xE2\x98\x61");
671/// let mut chars = vec![];
672/// while !bytes.is_empty() {
673/// let (ch, size) = decode_utf8_lossy(bytes);
674/// bytes = &bytes[size..];
675/// chars.push(ch);
676/// }
677/// assert_eq!(vec!['☃', '\u{FFFD}', '𝞃', '\u{FFFD}', 'a'], chars);
678/// ```
679#[inline]
680pub fn decode_lossy<B: AsRef<[u8]>>(slice: B) -> (char, usize) {
681 match decode(slice) {
682 (Some(ch), size) => (ch, size),
683 (None, size) => ('\u{FFFD}', size),
684 }
685}
686
687/// UTF-8 decode a single Unicode scalar value from the end of a slice.
688///
689/// When successful, the corresponding Unicode scalar value is returned along
690/// with the number of bytes it was encoded with. The number of bytes consumed
691/// for a successful decode is always between 1 and 4, inclusive.
692///
693/// When unsuccessful, `None` is returned along with the number of bytes that
694/// make up a maximal prefix of a valid UTF-8 code unit sequence. In this case,
695/// the number of bytes consumed is always between 0 and 3, inclusive, where
696/// 0 is only returned when `slice` is empty.
697///
698/// # Examples
699///
700/// Basic usage:
701///
702/// ```
703/// use bstr::decode_last_utf8;
704///
705/// // Decoding a valid codepoint.
706/// let (ch, size) = decode_last_utf8(b"\xE2\x98\x83");
707/// assert_eq!(Some('☃'), ch);
708/// assert_eq!(3, size);
709///
710/// // Decoding an incomplete codepoint.
711/// let (ch, size) = decode_last_utf8(b"\xE2\x98");
712/// assert_eq!(None, ch);
713/// assert_eq!(2, size);
714/// ```
715///
716/// This example shows how to iterate over all codepoints in UTF-8 encoded
717/// bytes in reverse, while replacing invalid UTF-8 sequences with the
718/// replacement codepoint:
719///
720/// ```
721/// use bstr::{B, decode_last_utf8};
722///
723/// let mut bytes = B(b"\xE2\x98\x83\xFF\xF0\x9D\x9E\x83\xE2\x98\x61");
724/// let mut chars = vec![];
725/// while !bytes.is_empty() {
726/// let (ch, size) = decode_last_utf8(bytes);
727/// bytes = &bytes[..bytes.len()-size];
728/// chars.push(ch.unwrap_or('\u{FFFD}'));
729/// }
730/// assert_eq!(vec!['a', '\u{FFFD}', '𝞃', '\u{FFFD}', '☃'], chars);
731/// ```
732#[inline]
733pub fn decode_last<B: AsRef<[u8]>>(slice: B) -> (Option<char>, usize) {
734 // TODO: We could implement this by reversing the UTF-8 automaton, but for
735 // now, we do it the slow way by using the forward automaton.
736
737 let slice = slice.as_ref();
738 if slice.is_empty() {
739 return (None, 0);
740 }
741 let mut start = slice.len() - 1;
742 let limit = slice.len().saturating_sub(4);
743 while start > limit && !is_leading_or_invalid_utf8_byte(slice[start]) {
744 start -= 1;
745 }
746 let (ch, size) = decode(&slice[start..]);
747 // If we didn't consume all of the bytes, then that means there's at least
748 // one stray byte that never occurs in a valid code unit prefix, so we can
749 // advance by one byte.
750 if start + size != slice.len() {
751 (None, 1)
752 } else {
753 (ch, size)
754 }
755}
756
757/// Lossily UTF-8 decode a single Unicode scalar value from the end of a slice.
758///
759/// When successful, the corresponding Unicode scalar value is returned along
760/// with the number of bytes it was encoded with. The number of bytes consumed
761/// for a successful decode is always between 1 and 4, inclusive.
762///
763/// When unsuccessful, the Unicode replacement codepoint (`U+FFFD`) is returned
764/// along with the number of bytes that make up a maximal prefix of a valid
765/// UTF-8 code unit sequence. In this case, the number of bytes consumed is
766/// always between 0 and 3, inclusive, where 0 is only returned when `slice` is
767/// empty.
768///
769/// # Examples
770///
771/// Basic usage:
772///
773/// ```ignore
774/// use bstr::decode_last_utf8_lossy;
775///
776/// // Decoding a valid codepoint.
777/// let (ch, size) = decode_last_utf8_lossy(b"\xE2\x98\x83");
778/// assert_eq!('☃', ch);
779/// assert_eq!(3, size);
780///
781/// // Decoding an incomplete codepoint.
782/// let (ch, size) = decode_last_utf8_lossy(b"\xE2\x98");
783/// assert_eq!('\u{FFFD}', ch);
784/// assert_eq!(2, size);
785/// ```
786///
787/// This example shows how to iterate over all codepoints in UTF-8 encoded
788/// bytes in reverse, while replacing invalid UTF-8 sequences with the
789/// replacement codepoint:
790///
791/// ```ignore
792/// use bstr::decode_last_utf8_lossy;
793///
794/// let mut bytes = B(b"\xE2\x98\x83\xFF\xF0\x9D\x9E\x83\xE2\x98\x61");
795/// let mut chars = vec![];
796/// while !bytes.is_empty() {
797/// let (ch, size) = decode_last_utf8_lossy(bytes);
798/// bytes = &bytes[..bytes.len()-size];
799/// chars.push(ch);
800/// }
801/// assert_eq!(vec!['a', '\u{FFFD}', '𝞃', '\u{FFFD}', '☃'], chars);
802/// ```
803#[inline]
804pub fn decode_last_lossy<B: AsRef<[u8]>>(slice: B) -> (char, usize) {
805 match decode_last(slice) {
806 (Some(ch), size) => (ch, size),
807 (None, size) => ('\u{FFFD}', size),
808 }
809}
810
811/// SAFETY: The decode function relies on state being equal to ACCEPT only if
812/// cp is a valid Unicode scalar value.
813#[inline]
814pub fn decode_step(state: &mut usize, cp: &mut u32, b: u8) {
815 let class = CLASSES[b as usize];
816 if *state == ACCEPT {
817 *cp = (0xFF >> class) & (b as u32);
818 } else {
819 *cp = (b as u32 & 0b111111) | (*cp << 6);
820 }
821 *state = STATES_FORWARD[*state + class as usize] as usize;
822}
823
824/// Returns true if and only if the given byte is either a valid leading UTF-8
825/// byte, or is otherwise an invalid byte that can never appear anywhere in a
826/// valid UTF-8 sequence.
827fn is_leading_or_invalid_utf8_byte(b: u8) -> bool {
828 // In the ASCII case, the most significant bit is never set. The leading
829 // byte of a 2/3/4-byte sequence always has the top two most significant
830 // bits set. For bytes that can never appear anywhere in valid UTF-8, this
831 // also returns true, since every such byte has its two most significant
832 // bits set:
833 //
834 // \xC0 :: 11000000
835 // \xC1 :: 11000001
836 // \xF5 :: 11110101
837 // \xF6 :: 11110110
838 // \xF7 :: 11110111
839 // \xF8 :: 11111000
840 // \xF9 :: 11111001
841 // \xFA :: 11111010
842 // \xFB :: 11111011
843 // \xFC :: 11111100
844 // \xFD :: 11111101
845 // \xFE :: 11111110
846 // \xFF :: 11111111
847 (b & 0b1100_0000) != 0b1000_0000
848}
849
850#[cfg(all(test, feature = "std"))]
851mod tests {
852 use core::char;
853
854 use alloc::{string::String, vec, vec::Vec};
855
856 use crate::{
857 ext_slice::{ByteSlice, B},
858 tests::LOSSY_TESTS,
859 utf8::{self, Utf8Error},
860 };
861
862 fn utf8e(valid_up_to: usize) -> Utf8Error {
863 Utf8Error { valid_up_to, error_len: None }
864 }
865
866 fn utf8e2(valid_up_to: usize, error_len: usize) -> Utf8Error {
867 Utf8Error { valid_up_to, error_len: Some(error_len) }
868 }
869
870 #[test]
871 #[cfg(not(miri))]
872 fn validate_all_codepoints() {
873 for i in 0..(0x10FFFF + 1) {
874 let cp = match char::from_u32(i) {
875 None => continue,
876 Some(cp) => cp,
877 };
878 let mut buf = [0; 4];
879 let s = cp.encode_utf8(&mut buf);
880 assert_eq!(Ok(()), utf8::validate(s.as_bytes()));
881 }
882 }
883
884 #[test]
885 fn validate_multiple_codepoints() {
886 assert_eq!(Ok(()), utf8::validate(b"abc"));
887 assert_eq!(Ok(()), utf8::validate(b"a\xE2\x98\x83a"));
888 assert_eq!(Ok(()), utf8::validate(b"a\xF0\x9D\x9C\xB7a"));
889 assert_eq!(Ok(()), utf8::validate(b"\xE2\x98\x83\xF0\x9D\x9C\xB7",));
890 assert_eq!(
891 Ok(()),
892 utf8::validate(b"a\xE2\x98\x83a\xF0\x9D\x9C\xB7a",)
893 );
894 assert_eq!(
895 Ok(()),
896 utf8::validate(b"\xEF\xBF\xBD\xE2\x98\x83\xEF\xBF\xBD",)
897 );
898 }
899
900 #[test]
901 fn validate_errors() {
902 // single invalid byte
903 assert_eq!(Err(utf8e2(0, 1)), utf8::validate(b"\xFF"));
904 // single invalid byte after ASCII
905 assert_eq!(Err(utf8e2(1, 1)), utf8::validate(b"a\xFF"));
906 // single invalid byte after 2 byte sequence
907 assert_eq!(Err(utf8e2(2, 1)), utf8::validate(b"\xCE\xB2\xFF"));
908 // single invalid byte after 3 byte sequence
909 assert_eq!(Err(utf8e2(3, 1)), utf8::validate(b"\xE2\x98\x83\xFF"));
910 // single invalid byte after 4 byte sequence
911 assert_eq!(Err(utf8e2(4, 1)), utf8::validate(b"\xF0\x9D\x9D\xB1\xFF"));
912
913 // An invalid 2-byte sequence with a valid 1-byte prefix.
914 assert_eq!(Err(utf8e2(0, 1)), utf8::validate(b"\xCE\xF0"));
915 // An invalid 3-byte sequence with a valid 2-byte prefix.
916 assert_eq!(Err(utf8e2(0, 2)), utf8::validate(b"\xE2\x98\xF0"));
917 // An invalid 4-byte sequence with a valid 3-byte prefix.
918 assert_eq!(Err(utf8e2(0, 3)), utf8::validate(b"\xF0\x9D\x9D\xF0"));
919
920 // An overlong sequence. Should be \xE2\x82\xAC, but we encode the
921 // same codepoint value in 4 bytes. This not only tests that we reject
922 // overlong sequences, but that we get valid_up_to correct.
923 assert_eq!(Err(utf8e2(0, 1)), utf8::validate(b"\xF0\x82\x82\xAC"));
924 assert_eq!(Err(utf8e2(1, 1)), utf8::validate(b"a\xF0\x82\x82\xAC"));
925 assert_eq!(
926 Err(utf8e2(3, 1)),
927 utf8::validate(b"\xE2\x98\x83\xF0\x82\x82\xAC",)
928 );
929
930 // Check that encoding a surrogate codepoint using the UTF-8 scheme
931 // fails validation.
932 assert_eq!(Err(utf8e2(0, 1)), utf8::validate(b"\xED\xA0\x80"));
933 assert_eq!(Err(utf8e2(1, 1)), utf8::validate(b"a\xED\xA0\x80"));
934 assert_eq!(
935 Err(utf8e2(3, 1)),
936 utf8::validate(b"\xE2\x98\x83\xED\xA0\x80",)
937 );
938
939 // Check that an incomplete 2-byte sequence fails.
940 assert_eq!(Err(utf8e2(0, 1)), utf8::validate(b"\xCEa"));
941 assert_eq!(Err(utf8e2(1, 1)), utf8::validate(b"a\xCEa"));
942 assert_eq!(
943 Err(utf8e2(3, 1)),
944 utf8::validate(b"\xE2\x98\x83\xCE\xE2\x98\x83",)
945 );
946 // Check that an incomplete 3-byte sequence fails.
947 assert_eq!(Err(utf8e2(0, 2)), utf8::validate(b"\xE2\x98a"));
948 assert_eq!(Err(utf8e2(1, 2)), utf8::validate(b"a\xE2\x98a"));
949 assert_eq!(
950 Err(utf8e2(3, 2)),
951 utf8::validate(b"\xE2\x98\x83\xE2\x98\xE2\x98\x83",)
952 );
953 // Check that an incomplete 4-byte sequence fails.
954 assert_eq!(Err(utf8e2(0, 3)), utf8::validate(b"\xF0\x9D\x9Ca"));
955 assert_eq!(Err(utf8e2(1, 3)), utf8::validate(b"a\xF0\x9D\x9Ca"));
956 assert_eq!(
957 Err(utf8e2(4, 3)),
958 utf8::validate(b"\xF0\x9D\x9C\xB1\xF0\x9D\x9C\xE2\x98\x83",)
959 );
960 assert_eq!(
961 Err(utf8e2(6, 3)),
962 utf8::validate(b"foobar\xF1\x80\x80quux",)
963 );
964
965 // Check that an incomplete (EOF) 2-byte sequence fails.
966 assert_eq!(Err(utf8e(0)), utf8::validate(b"\xCE"));
967 assert_eq!(Err(utf8e(1)), utf8::validate(b"a\xCE"));
968 assert_eq!(Err(utf8e(3)), utf8::validate(b"\xE2\x98\x83\xCE"));
969 // Check that an incomplete (EOF) 3-byte sequence fails.
970 assert_eq!(Err(utf8e(0)), utf8::validate(b"\xE2\x98"));
971 assert_eq!(Err(utf8e(1)), utf8::validate(b"a\xE2\x98"));
972 assert_eq!(Err(utf8e(3)), utf8::validate(b"\xE2\x98\x83\xE2\x98"));
973 // Check that an incomplete (EOF) 4-byte sequence fails.
974 assert_eq!(Err(utf8e(0)), utf8::validate(b"\xF0\x9D\x9C"));
975 assert_eq!(Err(utf8e(1)), utf8::validate(b"a\xF0\x9D\x9C"));
976 assert_eq!(
977 Err(utf8e(4)),
978 utf8::validate(b"\xF0\x9D\x9C\xB1\xF0\x9D\x9C",)
979 );
980
981 // Test that we errors correct even after long valid sequences. This
982 // checks that our "backup" logic for detecting errors is correct.
983 assert_eq!(
984 Err(utf8e2(8, 1)),
985 utf8::validate(b"\xe2\x98\x83\xce\xb2\xe3\x83\x84\xFF",)
986 );
987 }
988
989 #[test]
990 fn decode_valid() {
991 fn d(mut s: &str) -> Vec<char> {
992 let mut chars = vec![];
993 while !s.is_empty() {
994 let (ch, size) = utf8::decode(s.as_bytes());
995 s = &s[size..];
996 chars.push(ch.unwrap());
997 }
998 chars
999 }
1000
1001 assert_eq!(vec!['☃'], d("☃"));
1002 assert_eq!(vec!['☃', '☃'], d("☃☃"));
1003 assert_eq!(vec!['α', 'β', 'γ', 'δ', 'ε'], d("αβγδε"));
1004 assert_eq!(vec!['☃', '⛄', '⛇'], d("☃⛄⛇"));
1005 assert_eq!(vec!['𝗮', '𝗯', '𝗰', '𝗱', '𝗲'], d("𝗮𝗯𝗰𝗱𝗲"));
1006 }
1007
1008 #[test]
1009 fn decode_invalid() {
1010 let (ch, size) = utf8::decode(b"");
1011 assert_eq!(None, ch);
1012 assert_eq!(0, size);
1013
1014 let (ch, size) = utf8::decode(b"\xFF");
1015 assert_eq!(None, ch);
1016 assert_eq!(1, size);
1017
1018 let (ch, size) = utf8::decode(b"\xCE\xF0");
1019 assert_eq!(None, ch);
1020 assert_eq!(1, size);
1021
1022 let (ch, size) = utf8::decode(b"\xE2\x98\xF0");
1023 assert_eq!(None, ch);
1024 assert_eq!(2, size);
1025
1026 let (ch, size) = utf8::decode(b"\xF0\x9D\x9D");
1027 assert_eq!(None, ch);
1028 assert_eq!(3, size);
1029
1030 let (ch, size) = utf8::decode(b"\xF0\x9D\x9D\xF0");
1031 assert_eq!(None, ch);
1032 assert_eq!(3, size);
1033
1034 let (ch, size) = utf8::decode(b"\xF0\x82\x82\xAC");
1035 assert_eq!(None, ch);
1036 assert_eq!(1, size);
1037
1038 let (ch, size) = utf8::decode(b"\xED\xA0\x80");
1039 assert_eq!(None, ch);
1040 assert_eq!(1, size);
1041
1042 let (ch, size) = utf8::decode(b"\xCEa");
1043 assert_eq!(None, ch);
1044 assert_eq!(1, size);
1045
1046 let (ch, size) = utf8::decode(b"\xE2\x98a");
1047 assert_eq!(None, ch);
1048 assert_eq!(2, size);
1049
1050 let (ch, size) = utf8::decode(b"\xF0\x9D\x9Ca");
1051 assert_eq!(None, ch);
1052 assert_eq!(3, size);
1053 }
1054
1055 #[test]
1056 fn decode_lossy() {
1057 let (ch, size) = utf8::decode_lossy(b"");
1058 assert_eq!('\u{FFFD}', ch);
1059 assert_eq!(0, size);
1060
1061 let (ch, size) = utf8::decode_lossy(b"\xFF");
1062 assert_eq!('\u{FFFD}', ch);
1063 assert_eq!(1, size);
1064
1065 let (ch, size) = utf8::decode_lossy(b"\xCE\xF0");
1066 assert_eq!('\u{FFFD}', ch);
1067 assert_eq!(1, size);
1068
1069 let (ch, size) = utf8::decode_lossy(b"\xE2\x98\xF0");
1070 assert_eq!('\u{FFFD}', ch);
1071 assert_eq!(2, size);
1072
1073 let (ch, size) = utf8::decode_lossy(b"\xF0\x9D\x9D\xF0");
1074 assert_eq!('\u{FFFD}', ch);
1075 assert_eq!(3, size);
1076
1077 let (ch, size) = utf8::decode_lossy(b"\xF0\x82\x82\xAC");
1078 assert_eq!('\u{FFFD}', ch);
1079 assert_eq!(1, size);
1080
1081 let (ch, size) = utf8::decode_lossy(b"\xED\xA0\x80");
1082 assert_eq!('\u{FFFD}', ch);
1083 assert_eq!(1, size);
1084
1085 let (ch, size) = utf8::decode_lossy(b"\xCEa");
1086 assert_eq!('\u{FFFD}', ch);
1087 assert_eq!(1, size);
1088
1089 let (ch, size) = utf8::decode_lossy(b"\xE2\x98a");
1090 assert_eq!('\u{FFFD}', ch);
1091 assert_eq!(2, size);
1092
1093 let (ch, size) = utf8::decode_lossy(b"\xF0\x9D\x9Ca");
1094 assert_eq!('\u{FFFD}', ch);
1095 assert_eq!(3, size);
1096 }
1097
1098 #[test]
1099 fn decode_last_valid() {
1100 fn d(mut s: &str) -> Vec<char> {
1101 let mut chars = vec![];
1102 while !s.is_empty() {
1103 let (ch, size) = utf8::decode_last(s.as_bytes());
1104 s = &s[..s.len() - size];
1105 chars.push(ch.unwrap());
1106 }
1107 chars
1108 }
1109
1110 assert_eq!(vec!['☃'], d("☃"));
1111 assert_eq!(vec!['☃', '☃'], d("☃☃"));
1112 assert_eq!(vec!['ε', 'δ', 'γ', 'β', 'α'], d("αβγδε"));
1113 assert_eq!(vec!['⛇', '⛄', '☃'], d("☃⛄⛇"));
1114 assert_eq!(vec!['𝗲', '𝗱', '𝗰', '𝗯', '𝗮'], d("𝗮𝗯𝗰𝗱𝗲"));
1115 }
1116
1117 #[test]
1118 fn decode_last_invalid() {
1119 let (ch, size) = utf8::decode_last(b"");
1120 assert_eq!(None, ch);
1121 assert_eq!(0, size);
1122
1123 let (ch, size) = utf8::decode_last(b"\xFF");
1124 assert_eq!(None, ch);
1125 assert_eq!(1, size);
1126
1127 let (ch, size) = utf8::decode_last(b"\xCE\xF0");
1128 assert_eq!(None, ch);
1129 assert_eq!(1, size);
1130
1131 let (ch, size) = utf8::decode_last(b"\xCE");
1132 assert_eq!(None, ch);
1133 assert_eq!(1, size);
1134
1135 let (ch, size) = utf8::decode_last(b"\xE2\x98\xF0");
1136 assert_eq!(None, ch);
1137 assert_eq!(1, size);
1138
1139 let (ch, size) = utf8::decode_last(b"\xE2\x98");
1140 assert_eq!(None, ch);
1141 assert_eq!(2, size);
1142
1143 let (ch, size) = utf8::decode_last(b"\xF0\x9D\x9D\xF0");
1144 assert_eq!(None, ch);
1145 assert_eq!(1, size);
1146
1147 let (ch, size) = utf8::decode_last(b"\xF0\x9D\x9D");
1148 assert_eq!(None, ch);
1149 assert_eq!(3, size);
1150
1151 let (ch, size) = utf8::decode_last(b"\xF0\x82\x82\xAC");
1152 assert_eq!(None, ch);
1153 assert_eq!(1, size);
1154
1155 let (ch, size) = utf8::decode_last(b"\xED\xA0\x80");
1156 assert_eq!(None, ch);
1157 assert_eq!(1, size);
1158
1159 let (ch, size) = utf8::decode_last(b"\xED\xA0");
1160 assert_eq!(None, ch);
1161 assert_eq!(1, size);
1162
1163 let (ch, size) = utf8::decode_last(b"\xED");
1164 assert_eq!(None, ch);
1165 assert_eq!(1, size);
1166
1167 let (ch, size) = utf8::decode_last(b"a\xCE");
1168 assert_eq!(None, ch);
1169 assert_eq!(1, size);
1170
1171 let (ch, size) = utf8::decode_last(b"a\xE2\x98");
1172 assert_eq!(None, ch);
1173 assert_eq!(2, size);
1174
1175 let (ch, size) = utf8::decode_last(b"a\xF0\x9D\x9C");
1176 assert_eq!(None, ch);
1177 assert_eq!(3, size);
1178 }
1179
1180 #[test]
1181 fn decode_last_lossy() {
1182 let (ch, size) = utf8::decode_last_lossy(b"");
1183 assert_eq!('\u{FFFD}', ch);
1184 assert_eq!(0, size);
1185
1186 let (ch, size) = utf8::decode_last_lossy(b"\xFF");
1187 assert_eq!('\u{FFFD}', ch);
1188 assert_eq!(1, size);
1189
1190 let (ch, size) = utf8::decode_last_lossy(b"\xCE\xF0");
1191 assert_eq!('\u{FFFD}', ch);
1192 assert_eq!(1, size);
1193
1194 let (ch, size) = utf8::decode_last_lossy(b"\xCE");
1195 assert_eq!('\u{FFFD}', ch);
1196 assert_eq!(1, size);
1197
1198 let (ch, size) = utf8::decode_last_lossy(b"\xE2\x98\xF0");
1199 assert_eq!('\u{FFFD}', ch);
1200 assert_eq!(1, size);
1201
1202 let (ch, size) = utf8::decode_last_lossy(b"\xE2\x98");
1203 assert_eq!('\u{FFFD}', ch);
1204 assert_eq!(2, size);
1205
1206 let (ch, size) = utf8::decode_last_lossy(b"\xF0\x9D\x9D\xF0");
1207 assert_eq!('\u{FFFD}', ch);
1208 assert_eq!(1, size);
1209
1210 let (ch, size) = utf8::decode_last_lossy(b"\xF0\x9D\x9D");
1211 assert_eq!('\u{FFFD}', ch);
1212 assert_eq!(3, size);
1213
1214 let (ch, size) = utf8::decode_last_lossy(b"\xF0\x82\x82\xAC");
1215 assert_eq!('\u{FFFD}', ch);
1216 assert_eq!(1, size);
1217
1218 let (ch, size) = utf8::decode_last_lossy(b"\xED\xA0\x80");
1219 assert_eq!('\u{FFFD}', ch);
1220 assert_eq!(1, size);
1221
1222 let (ch, size) = utf8::decode_last_lossy(b"\xED\xA0");
1223 assert_eq!('\u{FFFD}', ch);
1224 assert_eq!(1, size);
1225
1226 let (ch, size) = utf8::decode_last_lossy(b"\xED");
1227 assert_eq!('\u{FFFD}', ch);
1228 assert_eq!(1, size);
1229
1230 let (ch, size) = utf8::decode_last_lossy(b"a\xCE");
1231 assert_eq!('\u{FFFD}', ch);
1232 assert_eq!(1, size);
1233
1234 let (ch, size) = utf8::decode_last_lossy(b"a\xE2\x98");
1235 assert_eq!('\u{FFFD}', ch);
1236 assert_eq!(2, size);
1237
1238 let (ch, size) = utf8::decode_last_lossy(b"a\xF0\x9D\x9C");
1239 assert_eq!('\u{FFFD}', ch);
1240 assert_eq!(3, size);
1241 }
1242
1243 #[test]
1244 fn chars() {
1245 for (i, &(expected, input)) in LOSSY_TESTS.iter().enumerate() {
1246 let got: String = B(input).chars().collect();
1247 assert_eq!(
1248 expected, got,
1249 "chars(ith: {:?}, given: {:?})",
1250 i, input,
1251 );
1252 let got: String =
1253 B(input).char_indices().map(|(_, _, ch)| ch).collect();
1254 assert_eq!(
1255 expected, got,
1256 "char_indices(ith: {:?}, given: {:?})",
1257 i, input,
1258 );
1259
1260 let expected: String = expected.chars().rev().collect();
1261
1262 let got: String = B(input).chars().rev().collect();
1263 assert_eq!(
1264 expected, got,
1265 "chars.rev(ith: {:?}, given: {:?})",
1266 i, input,
1267 );
1268 let got: String =
1269 B(input).char_indices().rev().map(|(_, _, ch)| ch).collect();
1270 assert_eq!(
1271 expected, got,
1272 "char_indices.rev(ith: {:?}, given: {:?})",
1273 i, input,
1274 );
1275 }
1276 }
1277
1278 #[test]
1279 fn utf8_chunks() {
1280 let mut c = utf8::Utf8Chunks { bytes: b"123\xC0" };
1281 assert_eq!(
1282 (c.next(), c.next()),
1283 (
1284 Some(utf8::Utf8Chunk {
1285 valid: "123",
1286 invalid: b"\xC0".as_bstr(),
1287 incomplete: false,
1288 }),
1289 None,
1290 )
1291 );
1292
1293 let mut c = utf8::Utf8Chunks { bytes: b"123\xFF\xFF" };
1294 assert_eq!(
1295 (c.next(), c.next(), c.next()),
1296 (
1297 Some(utf8::Utf8Chunk {
1298 valid: "123",
1299 invalid: b"\xFF".as_bstr(),
1300 incomplete: false,
1301 }),
1302 Some(utf8::Utf8Chunk {
1303 valid: "",
1304 invalid: b"\xFF".as_bstr(),
1305 incomplete: false,
1306 }),
1307 None,
1308 )
1309 );
1310
1311 let mut c = utf8::Utf8Chunks { bytes: b"123\xD0" };
1312 assert_eq!(
1313 (c.next(), c.next()),
1314 (
1315 Some(utf8::Utf8Chunk {
1316 valid: "123",
1317 invalid: b"\xD0".as_bstr(),
1318 incomplete: true,
1319 }),
1320 None,
1321 )
1322 );
1323
1324 let mut c = utf8::Utf8Chunks { bytes: b"123\xD0456" };
1325 assert_eq!(
1326 (c.next(), c.next(), c.next()),
1327 (
1328 Some(utf8::Utf8Chunk {
1329 valid: "123",
1330 invalid: b"\xD0".as_bstr(),
1331 incomplete: false,
1332 }),
1333 Some(utf8::Utf8Chunk {
1334 valid: "456",
1335 invalid: b"".as_bstr(),
1336 incomplete: false,
1337 }),
1338 None,
1339 )
1340 );
1341
1342 let mut c = utf8::Utf8Chunks { bytes: b"123\xE2\x98" };
1343 assert_eq!(
1344 (c.next(), c.next()),
1345 (
1346 Some(utf8::Utf8Chunk {
1347 valid: "123",
1348 invalid: b"\xE2\x98".as_bstr(),
1349 incomplete: true,
1350 }),
1351 None,
1352 )
1353 );
1354
1355 let mut c = utf8::Utf8Chunks { bytes: b"123\xF4\x8F\xBF" };
1356 assert_eq!(
1357 (c.next(), c.next()),
1358 (
1359 Some(utf8::Utf8Chunk {
1360 valid: "123",
1361 invalid: b"\xF4\x8F\xBF".as_bstr(),
1362 incomplete: true,
1363 }),
1364 None,
1365 )
1366 );
1367 }
1368}