/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 | } 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 | } |
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 | } |
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 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 | } |
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 | | } |