Coverage Report

Created: 2026-08-05 07:37

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