Skip to main content

encoding_rs/
mem.rs

1// Copyright Mozilla Foundation. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Functions for converting between different in-RAM representations of text
11//! and for quickly checking if the Unicode Bidirectional Algorithm can be
12//! avoided.
13//!
14//! By using slices for output, the functions here seek to enable by-register
15//! (ALU register or SIMD register as available) operations in order to
16//! outperform iterator-based conversions available in the Rust standard
17//! library.
18//!
19//! _Note:_ "Latin1" in this module refers to the Unicode range from U+0000 to
20//! U+00FF, inclusive, and does not refer to the windows-1252 range. This
21//! in-memory encoding is sometimes used as a storage optimization of text
22//! when UTF-16 indexing and length semantics are exposed.
23//!
24//! The FFI binding for this module are in the
25//! [encoding_c_mem crate](https://github.com/hsivonen/encoding_c_mem).
26
27#[cfg(feature = "alloc")]
28use alloc::borrow::Cow;
29#[cfg(feature = "alloc")]
30use alloc::string::String;
31#[cfg(feature = "alloc")]
32use alloc::vec::Vec;
33
34use super::DecoderResult;
35use super::in_inclusive_range8;
36use super::in_inclusive_range16;
37use super::in_inclusive_range32;
38use super::in_range16;
39use super::in_range32;
40use crate::ascii::*;
41use crate::utf_8::*;
42
43macro_rules! non_fuzz_debug_assert {
44    ($($arg:tt)*) => (if !cfg!(fuzzing) { debug_assert!($($arg)*); })
45}
46
47cfg_if! {
48    if #[cfg(feature = "simd-accel")] {
49        use ::core::intrinsics::likely;
50        use ::core::intrinsics::unlikely;
51    } else {
52        #[inline(always)]
53        fn likely(b: bool) -> bool {
54            b
55        }
56        #[inline(always)]
57        fn unlikely(b: bool) -> bool {
58            b
59        }
60    }
61}
62
63/// Classification of text as Latin1 (all code points are below U+0100),
64/// left-to-right with some non-Latin1 characters or as containing at least
65/// some right-to-left characters.
66#[must_use]
67#[derive(Debug, PartialEq, Eq)]
68#[repr(C)]
69pub enum Latin1Bidi {
70    /// Every character is below U+0100.
71    Latin1 = 0,
72    /// There is at least one character that's U+0100 or higher, but there
73    /// are no right-to-left characters.
74    LeftToRight = 1,
75    /// There is at least one right-to-left character.
76    Bidi = 2,
77}
78
79#[inline(always)]
80#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
81fn is_utf8_latin1_impl(buffer: &[u8]) -> Option<usize> {
82    let mut bytes = buffer;
83    let mut total = 0;
84    loop {
85        if let Some((byte, offset)) = validate_ascii(bytes) {
86            total += offset;
87            if in_inclusive_range8(byte, 0xC2, 0xC3) {
88                let next = offset + 1;
89                if next == bytes.len() {
90                    return Some(total);
91                }
92                if bytes[next] & 0xC0 != 0x80 {
93                    return Some(total);
94                }
95                bytes = &bytes[offset + 2..];
96                total += 2;
97            } else {
98                return Some(total);
99            }
100        } else {
101            return None;
102        }
103    }
104}
105
106macro_rules! copy_impl {
107    ($name:ident, $stride:ident, $src_unit:ty, $dst_unit:ty) => {
108        #[inline(always)]
109        fn $name(src: &[$src_unit], dst: &mut [$dst_unit]) {
110            // Make both the same length here to have the chunks and tail match.
111            let len = core::cmp::min(src.len(), dst.len());
112            let (src_strides, src_tail) = src[..len].as_chunks::<STRIDE>();
113            let (dst_strides, dst_tail) = dst[..len].as_chunks_mut::<STRIDE>();
114            let (src_double_strides, src_single_stride) = src_strides.as_chunks::<2>();
115            let (dst_double_strides, dst_single_stride) = dst_strides.as_chunks_mut::<2>();
116            for (src_double_stride, dst_double_stride) in
117                src_double_strides.iter().zip(dst_double_strides.iter_mut())
118            {
119                $stride(&src_double_stride[0], &mut dst_double_stride[0]);
120                $stride(&src_double_stride[1], &mut dst_double_stride[1]);
121            }
122            for (src_stride, dst_stride) in
123                src_single_stride.iter().zip(dst_single_stride.iter_mut())
124            {
125                $stride(src_stride, dst_stride);
126            }
127            for (src_slot, dst_slot) in src_tail.iter().zip(dst_tail.iter_mut()) {
128                *dst_slot = *src_slot as $dst_unit;
129            }
130        }
131    };
132}
133
134copy_impl!(unpack_latin1, unpack_stride, u8, u16);
135copy_impl!(pack_latin1, pack_stride, u16, u8);
136
137cfg_if! {
138    if #[cfg(all(
139        feature = "simd-accel",
140        target_endian = "little",
141    ))] {
142        use core::simd::u8x16;
143        use core::simd::u16x8;
144
145        use crate::simd_funcs::unpack_stride;
146        use crate::simd_funcs::pack_stride;
147
148        #[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
149        fn is_utf16_bidi_impl(buffer: &[u16]) -> bool {
150            let (half_strides, tail) = buffer.as_chunks::<{STRIDE / 2}>();
151            for half_stride in half_strides {
152                if crate::simd_funcs::is_half_stride_bidi(half_stride) {
153                    return true;
154                }
155            }
156            tail.iter().any(|c| is_utf16_code_unit_bidi(*c))
157        }
158
159        #[inline(always)]
160        fn is_str_latin1_bool_impl(buffer: &str) -> bool {
161            let (strides, tail) = buffer.as_bytes().as_chunks::<STRIDE>();
162            for stride in strides {
163                let simd = (*stride).into();
164                if !crate::simd_funcs::simd_is_str_latin1(simd) {
165                    return false;
166                }
167            }
168            tail.iter().all(|b| *b < 0xC4)
169        }
170
171        macro_rules! unit_check_impl {
172            ($name:ident, $stride:ident, $unit:ty, $simd:ty, $bound:expr) => {
173                #[inline(always)]
174                fn $name(buffer: &[$unit]) -> bool {
175                    // The most common reason to return `false` is for the first code
176                    // unit to fail the test, so check that first.
177                    if let Some(u) = buffer.first() {
178                        if *u >= $bound {
179                            return false;
180                        }
181                    }
182                    let (strides, tail) = buffer.as_chunks::<{STRIDE / core::mem::size_of::<$unit>()}>();
183                    let (quad_strides, strides_tail) = strides.as_chunks::<4>();
184                    for quad_stride in quad_strides {
185                        if let Some(reduced) = quad_stride.iter().map(|s| { let simd: $simd = (*s).into(); simd }).reduce(|a, b| a | b) {
186                            if !crate::simd_funcs::$stride(reduced) {
187                                return false;
188                            }
189                        } else {
190                            debug_assert!(false);
191                        }
192                    }
193                    if let Some(reduced) = strides_tail.iter().map(|s| { let simd: $simd = (*s).into(); simd }).reduce(|a, b| a | b) {
194                        if !crate::simd_funcs::$stride(reduced) {
195                            return false;
196                        }
197                    }
198                    if let Some(reduced) = tail.iter().copied().reduce(|a, b| a | b) {
199                        reduced < $bound
200                    } else {
201                        true
202                    }
203                }
204            };
205        }
206
207        unit_check_impl!(is_ascii_impl, simd_is_ascii, u8, u8x16, 0x80);
208        unit_check_impl!(is_basic_latin_impl, simd_is_basic_latin, u16, u16x8, 0x80);
209        unit_check_impl!(is_utf16_latin1_impl, simd_is_latin1, u16, u16x8, 0x100);
210
211        #[inline(always)]
212        fn check_utf16_for_latin1_and_bidi_impl(buffer: &[u16]) -> Latin1Bidi {
213            let (half_strides, tail) = buffer.as_chunks::<{STRIDE / 2}>();
214            let mut half_stride_iter = half_strides.iter();
215            loop {
216                let Some(s) = half_stride_iter.next() else {
217                    let mut iter = tail.iter();
218                    loop {
219                        let Some(u) = iter.next() else {
220                            return Latin1Bidi::Latin1;
221                        };
222                        if *u < 0x100 {
223                            continue;
224                        }
225                        if is_utf16_code_unit_bidi(*u) {
226                            return Latin1Bidi::Bidi;
227                        }
228                        loop {
229                            let Some(u) = iter.next() else {
230                                return Latin1Bidi::LeftToRight;
231                            };
232                            if is_utf16_code_unit_bidi(*u) {
233                                return Latin1Bidi::Bidi;
234                            }
235                        }
236                    }
237                };
238                let simd: u16x8 = (*s).into();
239                if crate::simd_funcs::simd_is_latin1(simd) {
240                    continue;
241                }
242                if crate::simd_funcs::is_u16x8_bidi(simd) {
243                    return Latin1Bidi::Bidi;
244                }
245                loop {
246                    let Some(s) = half_stride_iter.next() else {
247                        for u in tail {
248                            if is_utf16_code_unit_bidi(*u) {
249                                return Latin1Bidi::Bidi;
250                            }
251                        }
252                        return Latin1Bidi::LeftToRight;
253                    };
254                    let simd: u16x8 = (*s).into();
255                    if crate::simd_funcs::is_u16x8_bidi(simd) {
256                        return Latin1Bidi::Bidi;
257                    }
258                }
259            }
260        }
261
262    } else {
263        use crate::ascii::unpack_stride;
264        use crate::ascii::pack_stride;
265
266        fn is_utf16_bidi_impl(buffer: &[u16]) -> bool {
267            buffer.iter().any(|c| is_utf16_code_unit_bidi(*c))
268        }
269
270        #[inline(always)]
271        fn is_str_latin1_bool_impl(buffer: &str) -> bool {
272            is_str_latin1_impl(buffer).is_none()
273        }
274
275        macro_rules! unit_check_impl {
276            ($name:ident, $stride:ident, $unit:ty, $bound:expr) => {
277                #[inline(always)]
278                fn $name(buffer: &[$unit]) -> bool {
279                    let (strides, tail) = buffer.as_chunks::<STRIDE>();
280                    for stride in strides {
281                        if !crate::ascii::$stride(stride) {
282                            return false;
283                        }
284                    }
285                    if let Some(reduced) = tail.iter().copied().reduce(|a, b| a | b) {
286                        reduced < $bound
287                    } else {
288                        true
289                    }
290                }
291            };
292        }
293
294        unit_check_impl!(is_ascii_impl, is_ascii, u8, 0x80);
295        unit_check_impl!(is_basic_latin_impl, is_basic_latin, u16, 0x80);
296        unit_check_impl!(is_utf16_latin1_impl, is_utf16_latin1, u16, 0x100);
297
298        #[inline(always)]
299        fn check_utf16_for_latin1_and_bidi_impl(buffer: &[u16]) -> Latin1Bidi {
300            let mut iter = buffer.iter();
301            loop {
302                let Some(u) = iter.next() else {
303                    return Latin1Bidi::Latin1;
304                };
305                if *u < 0x100 {
306                    continue;
307                }
308                if is_utf16_code_unit_bidi(*u) {
309                    return Latin1Bidi::Bidi;
310                }
311                loop {
312                    let Some(u) = iter.next() else {
313                        return Latin1Bidi::LeftToRight;
314                    };
315                    if is_utf16_code_unit_bidi(*u) {
316                        return Latin1Bidi::Bidi;
317                    }
318                }
319            }
320        }
321
322    }
323}
324
325/// Checks whether the buffer is all-ASCII.
326///
327/// May read the entire buffer even if it isn't all-ASCII. (I.e. the function
328/// is not guaranteed to fail fast.)
329pub fn is_ascii(buffer: &[u8]) -> bool {
330    is_ascii_impl(buffer)
331}
332
333/// Checks whether the buffer is all-Basic Latin (i.e. UTF-16 representing
334/// only ASCII characters).
335///
336/// May read the entire buffer even if it isn't all-ASCII. (I.e. the function
337/// is not guaranteed to fail fast.)
338#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
339pub fn is_basic_latin(buffer: &[u16]) -> bool {
340    is_basic_latin_impl(buffer)
341}
342
343/// Checks whether the buffer is valid UTF-8 representing only code points
344/// less than or equal to U+00FF.
345///
346/// Fails fast. (I.e. returns before having read the whole buffer if UTF-8
347/// invalidity or code points above U+00FF are discovered.
348pub fn is_utf8_latin1(buffer: &[u8]) -> bool {
349    is_utf8_latin1_impl(buffer).is_none()
350}
351
352/// Checks whether the buffer represents only code points less than or equal
353/// to U+00FF.
354///
355/// Fails fast. (I.e. returns before having read the whole buffer if code
356/// points above U+00FF are discovered.
357pub fn is_str_latin1(buffer: &str) -> bool {
358    is_str_latin1_bool_impl(buffer)
359}
360
361/// Checks whether the buffer represents only code point less than or equal
362/// to U+00FF.
363///
364/// May read the entire buffer even if it isn't all-Latin1. (I.e. the function
365/// is not guaranteed to fail fast.)
366#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
367pub fn is_utf16_latin1(buffer: &[u16]) -> bool {
368    is_utf16_latin1_impl(buffer)
369}
370
371/// Checks whether a potentially-invalid UTF-8 buffer contains code points
372/// that trigger right-to-left processing.
373///
374/// The check is done on a Unicode block basis without regard to assigned
375/// vs. unassigned code points in the block. Hebrew presentation forms in
376/// the Alphabetic Presentation Forms block are treated as if they formed
377/// a block on their own (i.e. it treated as right-to-left). Additionally,
378/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
379/// for. Control characters that are technically bidi controls but do not
380/// cause right-to-left behavior without the presence of right-to-left
381/// characters or right-to-left controls are not checked for. As a special
382/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
383///
384/// Returns `true` if the input is invalid UTF-8 or the input contains an
385/// RTL character. Returns `false` if the input is valid UTF-8 and contains
386/// no RTL characters.
387#[allow(clippy::collapsible_if, clippy::cognitive_complexity)]
388#[inline]
389#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
390pub fn is_utf8_bidi(buffer: &[u8]) -> bool {
391    // As of rustc 1.25.0-nightly (73ac5d6a8 2018-01-11), this is faster
392    // than UTF-8 validation followed by `is_str_bidi()` for German,
393    // Russian and Japanese. However, this is considerably slower for Thai.
394    // Chances are that the compiler makes some branch predictions that are
395    // unfortunate for Thai. Not spending the time to manually optimize
396    // further at this time, since it's unclear if this variant even has
397    // use cases. However, this is worth revisiting once Rust gets the
398    // ability to annotate relative priorities of match arms.
399
400    // U+058F: D6 8F
401    // U+0590: D6 90
402    // U+08FF: E0 A3 BF
403    // U+0900: E0 A4 80
404    //
405    // U+200F: E2 80 8F
406    // U+202B: E2 80 AB
407    // U+202E: E2 80 AE
408    // U+2067: E2 81 A7
409    //
410    // U+FB1C: EF AC 9C
411    // U+FB1D: EF AC 9D
412    // U+FDFF: EF B7 BF
413    // U+FE00: EF B8 80
414    //
415    // U+FE6F: EF B9 AF
416    // U+FE70: EF B9 B0
417    // U+FEFE: EF BB BE
418    // U+FEFF: EF BB BF
419    //
420    // U+107FF: F0 90 9F BF
421    // U+10800: F0 90 A0 80
422    // U+10FFF: F0 90 BF BF
423    // U+11000: F0 91 80 80
424    //
425    // U+1E7FF: F0 9E 9F BF
426    // U+1E800: F0 9E A0 80
427    // U+1EFFF: F0 9E BF BF
428    // U+1F000: F0 9F 80 80
429    let mut src = buffer;
430    'outer: loop {
431        if let Some((mut byte, mut read)) = validate_ascii(src) {
432            // Check for the longest sequence to avoid checking twice for the
433            // multi-byte sequences.
434            if read + 4 <= src.len() {
435                'inner: loop {
436                    // At this point, `byte` is not included in `read`.
437                    match byte {
438                        0..=0x7F => {
439                            // ASCII: go back to SIMD.
440                            read += 1;
441                            src = &src[read..];
442                            continue 'outer;
443                        }
444                        0xC2..=0xD5 => {
445                            // Two-byte
446                            let second = unsafe { *(src.get_unchecked(read + 1)) };
447                            if !in_inclusive_range8(second, 0x80, 0xBF) {
448                                return true;
449                            }
450                            read += 2;
451                        }
452                        0xD6 => {
453                            // Two-byte
454                            let second = unsafe { *(src.get_unchecked(read + 1)) };
455                            if !in_inclusive_range8(second, 0x80, 0xBF) {
456                                return true;
457                            }
458                            // XXX consider folding the above and below checks
459                            if second > 0x8F {
460                                return true;
461                            }
462                            read += 2;
463                        }
464                        // two-byte starting with 0xD7 and above is bidi
465                        0xE1 | 0xE3..=0xEC | 0xEE => {
466                            // Three-byte normal
467                            let second = unsafe { *(src.get_unchecked(read + 1)) };
468                            let third = unsafe { *(src.get_unchecked(read + 2)) };
469                            if ((UTF8_DATA.table[usize::from(second)]
470                                & unsafe {
471                                    *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80))
472                                })
473                                | (third >> 6))
474                                != 2
475                            {
476                                return true;
477                            }
478                            read += 3;
479                        }
480                        0xE2 => {
481                            // Three-byte normal, potentially bidi
482                            let second = unsafe { *(src.get_unchecked(read + 1)) };
483                            let third = unsafe { *(src.get_unchecked(read + 2)) };
484                            if ((UTF8_DATA.table[usize::from(second)]
485                                & unsafe {
486                                    *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80))
487                                })
488                                | (third >> 6))
489                                != 2
490                            {
491                                return true;
492                            }
493                            if second == 0x80 {
494                                if third == 0x8F || third == 0xAB || third == 0xAE {
495                                    return true;
496                                }
497                            } else if second == 0x81 {
498                                if third == 0xA7 {
499                                    return true;
500                                }
501                            }
502                            read += 3;
503                        }
504                        0xEF => {
505                            // Three-byte normal, potentially bidi
506                            let second = unsafe { *(src.get_unchecked(read + 1)) };
507                            let third = unsafe { *(src.get_unchecked(read + 2)) };
508                            if ((UTF8_DATA.table[usize::from(second)]
509                                & unsafe {
510                                    *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80))
511                                })
512                                | (third >> 6))
513                                != 2
514                            {
515                                return true;
516                            }
517                            if in_inclusive_range8(second, 0xAC, 0xB7) {
518                                if second == 0xAC {
519                                    if third > 0x9C {
520                                        return true;
521                                    }
522                                } else {
523                                    return true;
524                                }
525                            } else if in_inclusive_range8(second, 0xB9, 0xBB) {
526                                if second == 0xB9 {
527                                    if third > 0xAF {
528                                        return true;
529                                    }
530                                } else if second == 0xBB {
531                                    if third != 0xBF {
532                                        return true;
533                                    }
534                                } else {
535                                    return true;
536                                }
537                            }
538                            read += 3;
539                        }
540                        0xE0 => {
541                            // Three-byte special lower bound, potentially bidi
542                            let second = unsafe { *(src.get_unchecked(read + 1)) };
543                            let third = unsafe { *(src.get_unchecked(read + 2)) };
544                            if ((UTF8_DATA.table[usize::from(second)]
545                                & unsafe {
546                                    *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80))
547                                })
548                                | (third >> 6))
549                                != 2
550                            {
551                                return true;
552                            }
553                            // XXX can this be folded into the above validity check
554                            if second < 0xA4 {
555                                return true;
556                            }
557                            read += 3;
558                        }
559                        0xED => {
560                            // Three-byte special upper bound
561                            let second = unsafe { *(src.get_unchecked(read + 1)) };
562                            let third = unsafe { *(src.get_unchecked(read + 2)) };
563                            if ((UTF8_DATA.table[usize::from(second)]
564                                & unsafe {
565                                    *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80))
566                                })
567                                | (third >> 6))
568                                != 2
569                            {
570                                return true;
571                            }
572                            read += 3;
573                        }
574                        0xF1..=0xF4 => {
575                            // Four-byte normal
576                            let second = unsafe { *(src.get_unchecked(read + 1)) };
577                            let third = unsafe { *(src.get_unchecked(read + 2)) };
578                            let fourth = unsafe { *(src.get_unchecked(read + 3)) };
579                            if (u16::from(
580                                UTF8_DATA.table[usize::from(second)]
581                                    & unsafe {
582                                        *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80))
583                                    },
584                            ) | u16::from(third >> 6)
585                                | (u16::from(fourth & 0xC0) << 2))
586                                != 0x202
587                            {
588                                return true;
589                            }
590                            read += 4;
591                        }
592                        0xF0 => {
593                            // Four-byte special lower bound, potentially bidi
594                            let second = unsafe { *(src.get_unchecked(read + 1)) };
595                            let third = unsafe { *(src.get_unchecked(read + 2)) };
596                            let fourth = unsafe { *(src.get_unchecked(read + 3)) };
597                            if (u16::from(
598                                UTF8_DATA.table[usize::from(second)]
599                                    & unsafe {
600                                        *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80))
601                                    },
602                            ) | u16::from(third >> 6)
603                                | (u16::from(fourth & 0xC0) << 2))
604                                != 0x202
605                            {
606                                return true;
607                            }
608                            if unlikely(second == 0x90 || second == 0x9E) {
609                                let third = src[read + 2];
610                                if third >= 0xA0 {
611                                    return true;
612                                }
613                            }
614                            read += 4;
615                        }
616                        _ => {
617                            // Invalid lead or bidi-only lead
618                            return true;
619                        }
620                    }
621                    if read + 4 > src.len() {
622                        if read == src.len() {
623                            return false;
624                        }
625                        byte = src[read];
626                        break 'inner;
627                    }
628                    byte = src[read];
629                    continue 'inner;
630                }
631            }
632            // We can't have a complete 4-byte sequence, but we could still have
633            // a complete shorter sequence.
634
635            // At this point, `byte` is not included in `read`.
636            match byte {
637                0..=0x7F => {
638                    // ASCII: go back to SIMD.
639                    read += 1;
640                    src = &src[read..];
641                    continue 'outer;
642                }
643                0xC2..=0xD5 => {
644                    // Two-byte
645                    let new_read = read + 2;
646                    if new_read > src.len() {
647                        return true;
648                    }
649                    let second = unsafe { *(src.get_unchecked(read + 1)) };
650                    if !in_inclusive_range8(second, 0x80, 0xBF) {
651                        return true;
652                    }
653                    read = new_read;
654                    // We need to deal with the case where we came here with 3 bytes
655                    // left, so we need to take a look at the last one.
656                    src = &src[read..];
657                    continue 'outer;
658                }
659                0xD6 => {
660                    // Two-byte, potentially bidi
661                    let new_read = read + 2;
662                    if new_read > src.len() {
663                        return true;
664                    }
665                    let second = unsafe { *(src.get_unchecked(read + 1)) };
666                    if !in_inclusive_range8(second, 0x80, 0xBF) {
667                        return true;
668                    }
669                    // XXX consider folding the above and below checks
670                    if second > 0x8F {
671                        return true;
672                    }
673                    read = new_read;
674                    // We need to deal with the case where we came here with 3 bytes
675                    // left, so we need to take a look at the last one.
676                    src = &src[read..];
677                    continue 'outer;
678                }
679                // two-byte starting with 0xD7 and above is bidi
680                0xE1 | 0xE3..=0xEC | 0xEE => {
681                    // Three-byte normal
682                    let new_read = read + 3;
683                    if new_read > src.len() {
684                        return true;
685                    }
686                    let second = unsafe { *(src.get_unchecked(read + 1)) };
687                    let third = unsafe { *(src.get_unchecked(read + 2)) };
688                    if ((UTF8_DATA.table[usize::from(second)]
689                        & unsafe { *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80)) })
690                        | (third >> 6))
691                        != 2
692                    {
693                        return true;
694                    }
695                }
696                0xE2 => {
697                    // Three-byte normal, potentially bidi
698                    let new_read = read + 3;
699                    if new_read > src.len() {
700                        return true;
701                    }
702                    let second = unsafe { *(src.get_unchecked(read + 1)) };
703                    let third = unsafe { *(src.get_unchecked(read + 2)) };
704                    if ((UTF8_DATA.table[usize::from(second)]
705                        & unsafe { *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80)) })
706                        | (third >> 6))
707                        != 2
708                    {
709                        return true;
710                    }
711                    if second == 0x80 {
712                        if third == 0x8F || third == 0xAB || third == 0xAE {
713                            return true;
714                        }
715                    } else if second == 0x81 {
716                        if third == 0xA7 {
717                            return true;
718                        }
719                    }
720                }
721                0xEF => {
722                    // Three-byte normal, potentially bidi
723                    let new_read = read + 3;
724                    if new_read > src.len() {
725                        return true;
726                    }
727                    let second = unsafe { *(src.get_unchecked(read + 1)) };
728                    let third = unsafe { *(src.get_unchecked(read + 2)) };
729                    if ((UTF8_DATA.table[usize::from(second)]
730                        & unsafe { *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80)) })
731                        | (third >> 6))
732                        != 2
733                    {
734                        return true;
735                    }
736                    if in_inclusive_range8(second, 0xAC, 0xB7) {
737                        if second == 0xAC {
738                            if third > 0x9C {
739                                return true;
740                            }
741                        } else {
742                            return true;
743                        }
744                    } else if in_inclusive_range8(second, 0xB9, 0xBB) {
745                        if second == 0xB9 {
746                            if third > 0xAF {
747                                return true;
748                            }
749                        } else if second == 0xBB {
750                            if third != 0xBF {
751                                return true;
752                            }
753                        } else {
754                            return true;
755                        }
756                    }
757                }
758                0xE0 => {
759                    // Three-byte special lower bound, potentially bidi
760                    let new_read = read + 3;
761                    if new_read > src.len() {
762                        return true;
763                    }
764                    let second = unsafe { *(src.get_unchecked(read + 1)) };
765                    let third = unsafe { *(src.get_unchecked(read + 2)) };
766                    if ((UTF8_DATA.table[usize::from(second)]
767                        & unsafe { *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80)) })
768                        | (third >> 6))
769                        != 2
770                    {
771                        return true;
772                    }
773                    // XXX can this be folded into the above validity check
774                    if second < 0xA4 {
775                        return true;
776                    }
777                }
778                0xED => {
779                    // Three-byte special upper bound
780                    let new_read = read + 3;
781                    if new_read > src.len() {
782                        return true;
783                    }
784                    let second = unsafe { *(src.get_unchecked(read + 1)) };
785                    let third = unsafe { *(src.get_unchecked(read + 2)) };
786                    if ((UTF8_DATA.table[usize::from(second)]
787                        & unsafe { *(UTF8_DATA.table.get_unchecked(byte as usize + 0x80)) })
788                        | (third >> 6))
789                        != 2
790                    {
791                        return true;
792                    }
793                }
794                _ => {
795                    // Invalid lead, 4-byte lead or 2-byte bidi-only lead
796                    return true;
797                }
798            }
799            return false;
800        } else {
801            return false;
802        }
803    }
804}
805
806/// Checks whether a valid UTF-8 buffer contains code points that trigger
807/// right-to-left processing.
808///
809/// The check is done on a Unicode block basis without regard to assigned
810/// vs. unassigned code points in the block. Hebrew presentation forms in
811/// the Alphabetic Presentation Forms block are treated as if they formed
812/// a block on their own (i.e. it treated as right-to-left). Additionally,
813/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
814/// for. Control characters that are technically bidi controls but do not
815/// cause right-to-left behavior without the presence of right-to-left
816/// characters or right-to-left controls are not checked for. As a special
817/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
818#[allow(clippy::collapsible_if)]
819#[inline]
820#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
821pub fn is_str_bidi(buffer: &str) -> bool {
822    // U+058F: D6 8F
823    // U+0590: D6 90
824    // U+08FF: E0 A3 BF
825    // U+0900: E0 A4 80
826    //
827    // U+200F: E2 80 8F
828    // U+202B: E2 80 AB
829    // U+202E: E2 80 AE
830    // U+2067: E2 81 A7
831    //
832    // U+FB1C: EF AC 9C
833    // U+FB1D: EF AC 9D
834    // U+FDFF: EF B7 BF
835    // U+FE00: EF B8 80
836    //
837    // U+FE6F: EF B9 AF
838    // U+FE70: EF B9 B0
839    // U+FEFE: EF BB BE
840    // U+FEFF: EF BB BF
841    //
842    // U+107FF: F0 90 9F BF
843    // U+10800: F0 90 A0 80
844    // U+10FFF: F0 90 BF BF
845    // U+11000: F0 91 80 80
846    //
847    // U+1E7FF: F0 9E 9F BF
848    // U+1E800: F0 9E A0 80
849    // U+1EFFF: F0 9E BF BF
850    // U+1F000: F0 9F 80 80
851    let mut bytes = buffer.as_bytes();
852    'outer: loop {
853        // TODO: Instead of just validating ASCII using SIMD, use SIMD
854        // to check for non-ASCII lead bytes, too, to quickly conclude
855        // that the vector consist entirely of CJK and below-Hebrew
856        // code points.
857        // Unfortunately, scripts above Arabic but below CJK share
858        // lead bytes with RTL.
859        if let Some((mut byte, mut read)) = validate_ascii(bytes) {
860            'inner: loop {
861                // At this point, `byte` is not included in `read`.
862                if byte < 0xE0 {
863                    if byte >= 0x80 {
864                        // Two-byte
865                        // Adding `unlikely` here improved throughput on
866                        // Russian plain text by 33%!
867                        if unlikely(byte >= 0xD6) {
868                            if byte == 0xD6 {
869                                let second = bytes[read + 1];
870                                if second > 0x8F {
871                                    return true;
872                                }
873                            } else {
874                                return true;
875                            }
876                        }
877                        read += 2;
878                    } else {
879                        // ASCII: write and go back to SIMD.
880                        read += 1;
881                        // Intuitively, we should go back to the outer loop only
882                        // if byte is 0x30 or above, so as to avoid trashing on
883                        // ASCII space, comma and period in non-Latin context.
884                        // However, the extra branch seems to cost more than it's
885                        // worth.
886                        bytes = &bytes[read..];
887                        continue 'outer;
888                    }
889                } else if byte < 0xF0 {
890                    // Three-byte
891                    if unlikely(!in_inclusive_range8(byte, 0xE3, 0xEE) && byte != 0xE1) {
892                        let second = bytes[read + 1];
893                        if byte == 0xE0 {
894                            if second < 0xA4 {
895                                return true;
896                            }
897                        } else if byte == 0xE2 {
898                            let third = bytes[read + 2];
899                            if second == 0x80 {
900                                if third == 0x8F || third == 0xAB || third == 0xAE {
901                                    return true;
902                                }
903                            } else if second == 0x81 {
904                                if third == 0xA7 {
905                                    return true;
906                                }
907                            }
908                        } else {
909                            debug_assert_eq!(byte, 0xEF);
910                            if in_inclusive_range8(second, 0xAC, 0xB7) {
911                                if second == 0xAC {
912                                    let third = bytes[read + 2];
913                                    if third > 0x9C {
914                                        return true;
915                                    }
916                                } else {
917                                    return true;
918                                }
919                            } else if in_inclusive_range8(second, 0xB9, 0xBB) {
920                                if second == 0xB9 {
921                                    let third = bytes[read + 2];
922                                    if third > 0xAF {
923                                        return true;
924                                    }
925                                } else if second == 0xBB {
926                                    let third = bytes[read + 2];
927                                    if third != 0xBF {
928                                        return true;
929                                    }
930                                } else {
931                                    return true;
932                                }
933                            }
934                        }
935                    }
936                    read += 3;
937                } else {
938                    // Four-byte
939                    let second = bytes[read + 1];
940                    if unlikely(byte == 0xF0 && (second == 0x90 || second == 0x9E)) {
941                        let third = bytes[read + 2];
942                        if third >= 0xA0 {
943                            return true;
944                        }
945                    }
946                    read += 4;
947                }
948                // The comparison is always < or == and never >, but including
949                // > here to let the compiler assume that < is true if this
950                // comparison is false.
951                if read >= bytes.len() {
952                    return false;
953                }
954                byte = bytes[read];
955                continue 'inner;
956            }
957        } else {
958            return false;
959        }
960    }
961}
962
963/// Checks whether a UTF-16 buffer contains code points that trigger
964/// right-to-left processing.
965///
966/// The check is done on a Unicode block basis without regard to assigned
967/// vs. unassigned code points in the block. Hebrew presentation forms in
968/// the Alphabetic Presentation Forms block are treated as if they formed
969/// a block on their own (i.e. it treated as right-to-left). Additionally,
970/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
971/// for. Control characters that are technically bidi controls but do not
972/// cause right-to-left behavior without the presence of right-to-left
973/// characters or right-to-left controls are not checked for. As a special
974/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
975///
976/// Returns `true` if the input contains an RTL character or an unpaired
977/// high surrogate that could be the high half of an RTL character.
978/// Returns `false` if the input contains neither RTL characters nor
979/// unpaired high surrogates that could be higher halves of RTL characters.
980pub fn is_utf16_bidi(buffer: &[u16]) -> bool {
981    is_utf16_bidi_impl(buffer)
982}
983
984/// Checks whether a scalar value triggers right-to-left processing.
985///
986/// The check is done on a Unicode block basis without regard to assigned
987/// vs. unassigned code points in the block. Hebrew presentation forms in
988/// the Alphabetic Presentation Forms block are treated as if they formed
989/// a block on their own (i.e. it treated as right-to-left). Additionally,
990/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
991/// for. Control characters that are technically bidi controls but do not
992/// cause right-to-left behavior without the presence of right-to-left
993/// characters or right-to-left controls are not checked for. As a special
994/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
995#[inline(always)]
996pub fn is_char_bidi(c: char) -> bool {
997    // Controls:
998    // Every control with RIGHT-TO-LEFT in its name in
999    // https://www.unicode.org/charts/PDF/U2000.pdf
1000    // U+200F RLM
1001    // U+202B RLE
1002    // U+202E RLO
1003    // U+2067 RLI
1004    //
1005    // BMP RTL:
1006    // https://www.unicode.org/roadmaps/bmp/
1007    // U+0590...U+08FF
1008    // U+FB1D...U+FDFF Hebrew presentation forms and
1009    //                 Arabic Presentation Forms A
1010    // U+FE70...U+FEFE Arabic Presentation Forms B (excl. BOM)
1011    //
1012    // Supplementary RTL:
1013    // https://www.unicode.org/roadmaps/smp/
1014    // U+10800...U+10FFF (Lead surrogate U+D802 or U+D803)
1015    // U+1E800...U+1EFFF (Lead surrogate U+D83A or U+D83B)
1016    let code_point = u32::from(c);
1017    if code_point < 0x0590 {
1018        // Below Hebrew
1019        return false;
1020    }
1021    if in_range32(code_point, 0x0900, 0xFB1D) {
1022        // Above Arabic Extended-A and below Hebrew presentation forms
1023        if in_inclusive_range32(code_point, 0x200F, 0x2067) {
1024            // In the range that contains the RTL controls
1025            return code_point == 0x200F
1026                || code_point == 0x202B
1027                || code_point == 0x202E
1028                || code_point == 0x2067;
1029        }
1030        return false;
1031    }
1032    if code_point > 0x1EFFF {
1033        // Above second astral RTL. (Emoji is here.)
1034        return false;
1035    }
1036    if in_range32(code_point, 0x11000, 0x1E800) {
1037        // Between astral RTL blocks
1038        return false;
1039    }
1040    if in_range32(code_point, 0xFEFF, 0x10800) {
1041        // Above Arabic Presentations Forms B (excl. BOM) and below first
1042        // astral RTL
1043        return false;
1044    }
1045    if in_range32(code_point, 0xFE00, 0xFE70) {
1046        // Between Arabic Presentations Forms
1047        return false;
1048    }
1049    true
1050}
1051
1052/// Checks whether a UTF-16 code unit triggers right-to-left processing.
1053///
1054/// The check is done on a Unicode block basis without regard to assigned
1055/// vs. unassigned code points in the block. Hebrew presentation forms in
1056/// the Alphabetic Presentation Forms block are treated as if they formed
1057/// a block on their own (i.e. it treated as right-to-left). Additionally,
1058/// the four RIGHT-TO-LEFT FOO controls in General Punctuation are checked
1059/// for. Control characters that are technically bidi controls but do not
1060/// cause right-to-left behavior without the presence of right-to-left
1061/// characters or right-to-left controls are not checked for. As a special
1062/// case, U+FEFF is excluded from Arabic Presentation Forms-B.
1063///
1064/// Since supplementary-plane right-to-left blocks are identifiable from the
1065/// high surrogate without examining the low surrogate, this function returns
1066/// `true` for such high surrogates making the function suitable for handling
1067/// supplementary-plane text without decoding surrogate pairs to scalar
1068/// values. Obviously, such high surrogates are then reported as right-to-left
1069/// even if actually unpaired.
1070#[inline(always)]
1071pub fn is_utf16_code_unit_bidi(u: u16) -> bool {
1072    if u < 0x0590 {
1073        // Below Hebrew
1074        return false;
1075    }
1076    if in_range16(u, 0x0900, 0xD802) {
1077        // Above Arabic Extended-A and below first RTL surrogate
1078        if in_inclusive_range16(u, 0x200F, 0x2067) {
1079            // In the range that contains the RTL controls
1080            return u == 0x200F || u == 0x202B || u == 0x202E || u == 0x2067;
1081        }
1082        return false;
1083    }
1084    if in_range16(u, 0xD83C, 0xFB1D) {
1085        // Between astral RTL high surrogates and Hebrew presentation forms
1086        // (Emoji is here)
1087        return false;
1088    }
1089    if in_range16(u, 0xD804, 0xD83A) {
1090        // Between RTL high surragates
1091        return false;
1092    }
1093    if u > 0xFEFE {
1094        // Above Arabic Presentation Forms (excl. BOM)
1095        return false;
1096    }
1097    if in_range16(u, 0xFE00, 0xFE70) {
1098        // Between Arabic Presentations Forms
1099        return false;
1100    }
1101    true
1102}
1103
1104/// Checks whether a potentially invalid UTF-8 buffer contains code points
1105/// that trigger right-to-left processing or is all-Latin1.
1106///
1107/// Possibly more efficient than performing the checks separately.
1108///
1109/// Returns `Latin1Bidi::Latin1` if `is_utf8_latin1()` would return `true`.
1110/// Otherwise, returns `Latin1Bidi::Bidi` if `is_utf8_bidi()` would return
1111/// `true`. Otherwise, returns `Latin1Bidi::LeftToRight`.
1112pub fn check_utf8_for_latin1_and_bidi(buffer: &[u8]) -> Latin1Bidi {
1113    if let Some(offset) = is_utf8_latin1_impl(buffer) {
1114        if is_utf8_bidi(&buffer[offset..]) {
1115            Latin1Bidi::Bidi
1116        } else {
1117            Latin1Bidi::LeftToRight
1118        }
1119    } else {
1120        Latin1Bidi::Latin1
1121    }
1122}
1123
1124/// Checks whether a valid UTF-8 buffer contains code points
1125/// that trigger right-to-left processing or is all-Latin1.
1126///
1127/// Possibly more efficient than performing the checks separately.
1128///
1129/// Returns `Latin1Bidi::Latin1` if `is_str_latin1()` would return `true`.
1130/// Otherwise, returns `Latin1Bidi::Bidi` if `is_str_bidi()` would return
1131/// `true`. Otherwise, returns `Latin1Bidi::LeftToRight`.
1132pub fn check_str_for_latin1_and_bidi(buffer: &str) -> Latin1Bidi {
1133    // The transition from the latin1 check to the bidi check isn't
1134    // optimal but not tweaking it to perfection today.
1135    if let Some(offset) = is_str_latin1_impl(buffer) {
1136        if is_str_bidi(&buffer[offset..]) {
1137            Latin1Bidi::Bidi
1138        } else {
1139            Latin1Bidi::LeftToRight
1140        }
1141    } else {
1142        Latin1Bidi::Latin1
1143    }
1144}
1145
1146/// Checks whether a potentially invalid UTF-16 buffer contains code points
1147/// that trigger right-to-left processing or is all-Latin1.
1148///
1149/// Possibly more efficient than performing the checks separately.
1150///
1151/// Returns `Latin1Bidi::Latin1` if `is_utf16_latin1()` would return `true`.
1152/// Otherwise, returns `Latin1Bidi::Bidi` if `is_utf16_bidi()` would return
1153/// `true`. Otherwise, returns `Latin1Bidi::LeftToRight`.
1154pub fn check_utf16_for_latin1_and_bidi(buffer: &[u16]) -> Latin1Bidi {
1155    check_utf16_for_latin1_and_bidi_impl(buffer)
1156}
1157
1158/// Converts potentially-invalid UTF-8 to valid UTF-16 with errors replaced
1159/// with the REPLACEMENT CHARACTER.
1160///
1161/// The length of the destination buffer must be at least the length of the
1162/// source buffer _plus one_.
1163///
1164/// Returns the number of `u16`s written.
1165///
1166/// # Panics
1167///
1168/// Panics if the destination buffer is shorter than stated above.
1169#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
1170pub fn convert_utf8_to_utf16(src: &[u8], dst: &mut [u16]) -> usize {
1171    // TODO: Can the requirement for dst to be at least one unit longer
1172    // be eliminated?
1173    assert!(dst.len() > src.len());
1174    let mut decoder = Utf8Decoder::new_inner();
1175    let mut total_read = 0usize;
1176    let mut total_written = 0usize;
1177    loop {
1178        let (result, read, written) =
1179            decoder.decode_to_utf16_raw(&src[total_read..], &mut dst[total_written..], true);
1180        total_read += read;
1181        total_written += written;
1182        match result {
1183            DecoderResult::InputEmpty => {
1184                return total_written;
1185            }
1186            DecoderResult::OutputFull => {
1187                unreachable!("The assert at the top of the function should have caught this.");
1188            }
1189            DecoderResult::Malformed(_, _) => {
1190                // There should always be space for the U+FFFD, because
1191                // otherwise we'd have gotten OutputFull already.
1192                dst[total_written] = 0xFFFD;
1193                total_written += 1;
1194            }
1195        }
1196    }
1197}
1198
1199/// Converts valid UTF-8 to valid UTF-16.
1200///
1201/// The length of the destination buffer must be at least the length of the
1202/// source buffer.
1203///
1204/// Returns the number of `u16`s written.
1205///
1206/// # Panics
1207///
1208/// Panics if the destination buffer is shorter than stated above.
1209#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
1210pub fn convert_str_to_utf16(src: &str, dst: &mut [u16]) -> usize {
1211    assert!(
1212        dst.len() >= src.len(),
1213        "Destination must not be shorter than the source."
1214    );
1215    let bytes = src.as_bytes();
1216    let mut read = 0;
1217    let mut written = 0;
1218    'outer: loop {
1219        let mut byte = {
1220            let src_remaining = &bytes[read..];
1221            let dst_remaining = &mut dst[written..];
1222            let length = src_remaining.len();
1223            match ascii_to_basic_latin(src_remaining, dst_remaining) {
1224                None => {
1225                    written += length;
1226                    return written;
1227                }
1228                Some((non_ascii, consumed)) => {
1229                    read += consumed;
1230                    written += consumed;
1231                    non_ascii
1232                }
1233            }
1234        };
1235        'inner: loop {
1236            // At this point, `byte` is not included in `read`.
1237            if byte < 0xE0 {
1238                if byte >= 0x80 {
1239                    // Two-byte
1240                    let second = unsafe { *(bytes.get_unchecked(read + 1)) };
1241                    let point = ((u16::from(byte) & 0x1F) << 6) | (u16::from(second) & 0x3F);
1242                    unsafe { *(dst.get_unchecked_mut(written)) = point };
1243                    read += 2;
1244                    written += 1;
1245                } else {
1246                    // ASCII: write and go back to SIMD.
1247                    unsafe { *(dst.get_unchecked_mut(written)) = u16::from(byte) };
1248                    read += 1;
1249                    written += 1;
1250                    // Intuitively, we should go back to the outer loop only
1251                    // if byte is 0x30 or above, so as to avoid trashing on
1252                    // ASCII space, comma and period in non-Latin context.
1253                    // However, the extra branch seems to cost more than it's
1254                    // worth.
1255                    continue 'outer;
1256                }
1257            } else if byte < 0xF0 {
1258                // Three-byte
1259                let second = unsafe { *(bytes.get_unchecked(read + 1)) };
1260                let third = unsafe { *(bytes.get_unchecked(read + 2)) };
1261                let point = ((u16::from(byte) & 0xF) << 12)
1262                    | ((u16::from(second) & 0x3F) << 6)
1263                    | (u16::from(third) & 0x3F);
1264                unsafe { *(dst.get_unchecked_mut(written)) = point };
1265                read += 3;
1266                written += 1;
1267            } else {
1268                // Four-byte
1269                let second = unsafe { *(bytes.get_unchecked(read + 1)) };
1270                let third = unsafe { *(bytes.get_unchecked(read + 2)) };
1271                let fourth = unsafe { *(bytes.get_unchecked(read + 3)) };
1272                let point = ((u32::from(byte) & 0x7) << 18)
1273                    | ((u32::from(second) & 0x3F) << 12)
1274                    | ((u32::from(third) & 0x3F) << 6)
1275                    | (u32::from(fourth) & 0x3F);
1276                unsafe { *(dst.get_unchecked_mut(written)) = (0xD7C0 + (point >> 10)) as u16 };
1277                unsafe {
1278                    *(dst.get_unchecked_mut(written + 1)) = (0xDC00 + (point & 0x3FF)) as u16
1279                };
1280                read += 4;
1281                written += 2;
1282            }
1283            // The comparison is always < or == and never >, but including
1284            // > here to let the compiler assume that < is true if this
1285            // comparison is false.
1286            if read >= src.len() {
1287                return written;
1288            }
1289            byte = bytes[read];
1290            continue 'inner;
1291        }
1292    }
1293}
1294
1295/// Converts potentially-invalid UTF-8 to valid UTF-16 signaling on error.
1296///
1297/// The length of the destination buffer must be at least the length of the
1298/// source buffer.
1299///
1300/// Returns the number of `u16`s written or `None` if the input was invalid.
1301///
1302/// When the input was invalid, some output may have been written.
1303///
1304/// # Panics
1305///
1306/// Panics if the destination buffer is shorter than stated above.
1307pub fn convert_utf8_to_utf16_without_replacement(src: &[u8], dst: &mut [u16]) -> Option<usize> {
1308    assert!(
1309        dst.len() >= src.len(),
1310        "Destination must not be shorter than the source."
1311    );
1312    let (read, written) = convert_utf8_to_utf16_up_to_invalid(src, dst);
1313    if read == src.len() {
1314        return Some(written);
1315    }
1316    None
1317}
1318
1319/// Converts potentially-invalid UTF-16 to valid UTF-8 with errors replaced
1320/// with the REPLACEMENT CHARACTER with potentially insufficient output
1321/// space.
1322///
1323/// Returns the number of code units read and the number of bytes written.
1324///
1325/// Guarantees that the bytes in the destination beyond the number of
1326/// bytes claimed as written by the second item of the return tuple
1327/// are left unmodified.
1328///
1329/// Not all code units are read if there isn't enough output space.
1330///
1331/// Note  that this method isn't designed for general streamability but for
1332/// not allocating memory for the worst case up front. Specifically,
1333/// if the input starts with or ends with an unpaired surrogate, those are
1334/// replaced with the REPLACEMENT CHARACTER.
1335///
1336/// Matches the semantics of `TextEncoder.encodeInto()` from the
1337/// Encoding Standard.
1338///
1339/// # Safety
1340///
1341/// This function may write garbage to bytes in the destination
1342/// after the reported number of written bytes, so if you want to
1343/// convert into a `&mut str`, use `convert_utf16_to_str_partial()`
1344/// instead of using this function together with the `unsafe`
1345/// method `as_bytes_mut()` on `&mut str`.
1346#[inline(always)]
1347pub fn convert_utf16_to_utf8_partial(src: &[u16], dst: &mut [u8]) -> (usize, usize) {
1348    // The two functions called below are marked `inline(never)` to make
1349    // transitions from the hot part (first function) into the cold part
1350    // (second function) go through a return and another call to discourage
1351    // the CPU from speculating from the hot code into the cold code.
1352    // Letting the transitions be mere intra-function jumps, even to
1353    // basic blocks out-of-lined to the end of the function would wipe
1354    // away a quarter of Arabic encode performance on Haswell!
1355    // As of 2026, making even the first function `inline(never)`
1356    // is still needed on Zen 3!
1357    let (read, written) = convert_utf16_to_utf8_partial_inner(src, dst);
1358    if likely(read == src.len()) {
1359        return (read, written);
1360    }
1361    let (tail_read, tail_written) =
1362        convert_utf16_to_utf8_partial_tail(&src[read..], &mut dst[written..]);
1363    (read + tail_read, written + tail_written)
1364}
1365
1366/// Converts potentially-invalid UTF-16 to valid UTF-8 with errors replaced
1367/// with the REPLACEMENT CHARACTER.
1368///
1369/// The length of the destination buffer must be at least the length of the
1370/// source buffer times three.
1371///
1372/// Returns the number of bytes written.
1373///
1374/// # Panics
1375///
1376/// Panics if the destination buffer is shorter than stated above.
1377///
1378/// # Safety
1379///
1380/// If you want to convert into a `&mut str`, use `convert_utf16_to_str()`
1381/// instead of using this function together with the `unsafe` method
1382/// `as_bytes_mut()` on `&mut str`.
1383#[inline(always)]
1384pub fn convert_utf16_to_utf8(src: &[u16], dst: &mut [u8]) -> usize {
1385    assert!(dst.len() >= src.len() * 3);
1386    let (read, written) = convert_utf16_to_utf8_partial(src, dst);
1387    debug_assert_eq!(read, src.len());
1388    written
1389}
1390
1391/// Converts potentially-invalid UTF-16 to valid UTF-8 with errors replaced
1392/// with the REPLACEMENT CHARACTER such that the validity of the output is
1393/// signaled using the Rust type system with potentially insufficient output
1394/// space.
1395///
1396/// Returns the number of code units read and the number of bytes written.
1397///
1398/// Not all code units are read if there isn't enough output space.
1399///
1400/// Note  that this method isn't designed for general streamability but for
1401/// not allocating memory for the worst case up front. Specifically,
1402/// if the input starts with or ends with an unpaired surrogate, those are
1403/// replaced with the REPLACEMENT CHARACTER.
1404pub fn convert_utf16_to_str_partial(src: &[u16], dst: &mut str) -> (usize, usize) {
1405    // SAFETY: We trust that `convert_utf16_to_utf8_partial` writes
1406    // valid UTF-8. To make the part of the slice after what was reported
1407    // as logically written by that funtion, we use knowledge of the internals
1408    // to overwrite trailing garbage that may have been written. Then we also
1409    // overwrite a possible partial UTF-8 byte sequence after that. Then the
1410    // rest must be valid on the assumption that `dst` was valid to begin with.
1411    // In case of a panic, the `ScopeGuard` zeros the whole slice, which ensures
1412    // it's valid UTF-8 in an use-after-panic scenario when unwinding is enabled.
1413    // (Relevant only if there's a panic due to a crate-internal bug. Panics
1414    // arising from misuse of the public API don't need this guard and end up
1415    // zeroing the slice unnecessarily.)
1416    let mut bytes = scopeguard::guard(unsafe { dst.as_bytes_mut() }, |bytes| {
1417        bytes.iter_mut().for_each(|b| *b = 0)
1418    });
1419    let (read, written) = convert_utf16_to_utf8_partial(src, &mut bytes);
1420    let len = bytes.len();
1421    let mut trail = written;
1422    while trail < len && ((bytes[trail] & 0xC0) == 0x80) {
1423        bytes[trail] = 0;
1424        trail += 1;
1425    }
1426    // Defuse the zeroing guard.
1427    let _ = scopeguard::ScopeGuard::<&mut [u8], _>::into_inner(bytes);
1428    (read, written)
1429}
1430
1431/// Converts potentially-invalid UTF-16 to valid UTF-8 with errors replaced
1432/// with the REPLACEMENT CHARACTER such that the validity of the output is
1433/// signaled using the Rust type system.
1434///
1435/// The length of the destination buffer must be at least the length of the
1436/// source buffer times three.
1437///
1438/// Returns the number of bytes written.
1439///
1440/// # Panics
1441///
1442/// Panics if the destination buffer is shorter than stated above.
1443#[inline(always)]
1444pub fn convert_utf16_to_str(src: &[u16], dst: &mut str) -> usize {
1445    assert!(dst.len() >= src.len() * 3);
1446    let (read, written) = convert_utf16_to_str_partial(src, dst);
1447    debug_assert_eq!(read, src.len());
1448    written
1449}
1450
1451/// Converts bytes whose unsigned value is interpreted as Unicode code point
1452/// (i.e. U+0000 to U+00FF, inclusive) to UTF-16.
1453///
1454/// The length of the destination buffer must be at least the length of the
1455/// source buffer.
1456///
1457/// The number of `u16`s written equals the length of the source buffer.
1458///
1459/// # Panics
1460///
1461/// Panics if the destination buffer is shorter than stated above.
1462#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
1463pub fn convert_latin1_to_utf16(src: &[u8], dst: &mut [u16]) {
1464    assert!(
1465        dst.len() >= src.len(),
1466        "Destination must not be shorter than the source."
1467    );
1468    unpack_latin1(src, dst);
1469}
1470
1471/// Converts bytes whose unsigned value is interpreted as Unicode code point
1472/// (i.e. U+0000 to U+00FF, inclusive) to UTF-8 with potentially insufficient
1473/// output space.
1474///
1475/// Returns the number of bytes read and the number of bytes written.
1476///
1477/// If the output isn't large enough, not all input is consumed.
1478///
1479/// # Safety
1480///
1481/// If you want to convert into a `&mut str`, use
1482/// `convert_latin1_to_str_partial()` instead of using this function
1483/// together with the `unsafe` method `as_bytes_mut()` on `&mut str`.
1484pub fn convert_latin1_to_utf8_partial(src: &[u8], dst: &mut [u8]) -> (usize, usize) {
1485    let src_len = src.len();
1486    let dst_len = dst.len();
1487    let mut total_read = 0usize;
1488    let mut total_written = 0usize;
1489    loop {
1490        // src can't advance more than dst
1491        let src_left = src_len - total_read;
1492        let dst_left = dst_len - total_written;
1493        let min_left = ::core::cmp::min(src_left, dst_left);
1494        if let Some((non_ascii, consumed)) =
1495            { ascii_to_ascii(&src[total_read..], &mut dst[total_written..]) }
1496        {
1497            total_read += consumed;
1498            total_written += consumed;
1499            if total_written.checked_add(2).unwrap() > dst_len {
1500                return (total_read, total_written);
1501            }
1502
1503            total_read += 1; // consume `non_ascii`
1504
1505            dst[total_written] = (non_ascii >> 6) | 0xC0;
1506            total_written += 1;
1507            dst[total_written] = (non_ascii & 0x3F) | 0x80;
1508            total_written += 1;
1509            continue;
1510        }
1511        return (total_read + min_left, total_written + min_left);
1512    }
1513}
1514
1515/// Converts bytes whose unsigned value is interpreted as Unicode code point
1516/// (i.e. U+0000 to U+00FF, inclusive) to UTF-8.
1517///
1518/// The length of the destination buffer must be at least the length of the
1519/// source buffer times two.
1520///
1521/// Returns the number of bytes written.
1522///
1523/// # Panics
1524///
1525/// Panics if the destination buffer is shorter than stated above.
1526///
1527/// # Safety
1528///
1529/// Note that this function may write garbage beyond the number of bytes
1530/// indicated by the return value, so using a `&mut str` interpreted as
1531/// `&mut [u8]` as the destination is not safe. If you want to convert into
1532/// a `&mut str`, use `convert_utf16_to_str()` instead of this function.
1533#[inline]
1534pub fn convert_latin1_to_utf8(src: &[u8], dst: &mut [u8]) -> usize {
1535    assert!(
1536        dst.len() >= src.len() * 2,
1537        "Destination must not be shorter than the source times two."
1538    );
1539    let (read, written) = convert_latin1_to_utf8_partial(src, dst);
1540    debug_assert_eq!(read, src.len());
1541    written
1542}
1543
1544/// Converts bytes whose unsigned value is interpreted as Unicode code point
1545/// (i.e. U+0000 to U+00FF, inclusive) to UTF-8 such that the validity of the
1546/// output is signaled using the Rust type system with potentially insufficient
1547/// output space.
1548///
1549/// Returns the number of bytes read and the number of bytes written.
1550///
1551/// If the output isn't large enough, not all input is consumed.
1552#[inline]
1553pub fn convert_latin1_to_str_partial(src: &[u8], dst: &mut str) -> (usize, usize) {
1554    let bytes: &mut [u8] = unsafe { dst.as_bytes_mut() };
1555    let (read, written) = convert_latin1_to_utf8_partial(src, bytes);
1556    let len = bytes.len();
1557    let mut trail = written;
1558    let max = ::core::cmp::min(len, trail + MAX_STRIDE_SIZE);
1559    while trail < max {
1560        bytes[trail] = 0;
1561        trail += 1;
1562    }
1563    while trail < len && ((bytes[trail] & 0xC0) == 0x80) {
1564        bytes[trail] = 0;
1565        trail += 1;
1566    }
1567    (read, written)
1568}
1569
1570/// Converts bytes whose unsigned value is interpreted as Unicode code point
1571/// (i.e. U+0000 to U+00FF, inclusive) to UTF-8 such that the validity of the
1572/// output is signaled using the Rust type system.
1573///
1574/// The length of the destination buffer must be at least the length of the
1575/// source buffer times two.
1576///
1577/// Returns the number of bytes written.
1578///
1579/// # Panics
1580///
1581/// Panics if the destination buffer is shorter than stated above.
1582#[inline]
1583pub fn convert_latin1_to_str(src: &[u8], dst: &mut str) -> usize {
1584    assert!(
1585        dst.len() >= src.len() * 2,
1586        "Destination must not be shorter than the source times two."
1587    );
1588    let (read, written) = convert_latin1_to_str_partial(src, dst);
1589    debug_assert_eq!(read, src.len());
1590    written
1591}
1592
1593/// If the input is valid UTF-8 representing only Unicode code points from
1594/// U+0000 to U+00FF, inclusive, converts the input into output that
1595/// represents the value of each code point as the unsigned byte value of
1596/// each output byte.
1597///
1598/// If the input does not fulfill the condition stated above, this function
1599/// panics if debug assertions are enabled (and fuzzing isn't) and otherwise
1600/// does something that is memory-safe without any promises about any
1601/// properties of the output. In particular, callers shouldn't assume the
1602/// output to be the same across crate versions or CPU architectures and
1603/// should not assume that non-ASCII input can't map to ASCII output.
1604///
1605/// The length of the destination buffer must be at least the length of the
1606/// source buffer.
1607///
1608/// Returns the number of bytes written.
1609///
1610/// # Panics
1611///
1612/// Panics if the destination buffer is shorter than stated above.
1613///
1614/// If debug assertions are enabled (and not fuzzing) and the input is
1615/// not in the range U+0000 to U+00FF, inclusive.
1616pub fn convert_utf8_to_latin1_lossy(src: &[u8], dst: &mut [u8]) -> usize {
1617    assert!(
1618        dst.len() >= src.len(),
1619        "Destination must not be shorter than the source."
1620    );
1621    non_fuzz_debug_assert!(is_utf8_latin1(src));
1622    let src_len = src.len();
1623    let mut total_read = 0usize;
1624    let mut total_written = 0usize;
1625    loop {
1626        // dst can't advance more than src
1627        let src_left = src_len - total_read;
1628        if let Some((non_ascii, consumed)) =
1629            { ascii_to_ascii(&src[total_read..], &mut dst[total_written..]) }
1630        {
1631            total_read += consumed + 1;
1632            total_written += consumed;
1633
1634            if total_read == src_len {
1635                return total_written;
1636            }
1637
1638            let trail = src[total_read];
1639            total_read += 1;
1640
1641            dst[total_written] = ((non_ascii & 0x1F) << 6) | (trail & 0x3F);
1642            total_written += 1;
1643            continue;
1644        }
1645        return total_written + src_left;
1646    }
1647}
1648
1649/// If the input is valid UTF-16 representing only Unicode code points from
1650/// U+0000 to U+00FF, inclusive, converts the input into output that
1651/// represents the value of each code point as the unsigned byte value of
1652/// each output byte.
1653///
1654/// If the input does not fulfill the condition stated above, does something
1655/// that is memory-safe without any promises about any properties of the
1656/// output and will probably assert in debug builds in future versions.
1657/// In particular, callers shouldn't assume the output to be the same across
1658/// crate versions or CPU architectures and should not assume that non-ASCII
1659/// input can't map to ASCII output.
1660///
1661/// The length of the destination buffer must be at least the length of the
1662/// source buffer.
1663///
1664/// The number of bytes written equals the length of the source buffer.
1665///
1666/// # Panics
1667///
1668/// Panics if the destination buffer is shorter than stated above.
1669///
1670/// (Probably in future versions if debug assertions are enabled (and not
1671/// fuzzing) and the input is not in the range U+0000 to U+00FF, inclusive.)
1672pub fn convert_utf16_to_latin1_lossy(src: &[u16], dst: &mut [u8]) {
1673    assert!(
1674        dst.len() >= src.len(),
1675        "Destination must not be shorter than the source."
1676    );
1677    // non_fuzz_debug_assert!(is_utf16_latin1(src));
1678    pack_latin1(src, dst);
1679}
1680
1681/// Converts bytes whose unsigned value is interpreted as Unicode code point
1682/// (i.e. U+0000 to U+00FF, inclusive) to UTF-8.
1683///
1684/// Borrows if input is ASCII-only. Performs a single heap allocation
1685/// otherwise.
1686///
1687/// Only available if the `alloc` feature is enabled (enabled by default).
1688#[cfg(feature = "alloc")]
1689pub fn decode_latin1<'a>(bytes: &'a [u8]) -> Cow<'a, str> {
1690    let up_to = ascii_valid_up_to(bytes);
1691    // >= makes later things optimize better than ==
1692    if up_to >= bytes.len() {
1693        debug_assert_eq!(up_to, bytes.len());
1694        // SAFETY: We've checked that `bytes` is valid UTF-8, since
1695        // it's ASCII.
1696        let s: &str = unsafe { ::core::str::from_utf8_unchecked(bytes) };
1697        return Cow::Borrowed(s);
1698    }
1699    let (head, tail) = bytes.split_at(up_to);
1700    let capacity = head.len() + tail.len() * 2;
1701    let mut vec = Vec::with_capacity(capacity);
1702    vec.extend_from_slice(head);
1703    let old_len = vec.len();
1704    let spare_capacity = crate::minimally_init(vec.spare_capacity_mut());
1705    debug_assert_eq!(old_len, up_to);
1706    let written = convert_latin1_to_utf8(tail, spare_capacity);
1707    debug_assert!(written <= spare_capacity.len());
1708    let new_len = old_len + written;
1709    assert!(new_len <= vec.capacity());
1710    // SAFETY: We trust that `convert_latin1_to_utf8` wrote valid UTF-8
1711    // to `spare_capacity[..written]`. Also, regarding the information
1712    // disclosure risk of `minimally_init`, this also means trusting
1713    // that every byte of `spare_capacity[..written]` got overwritten.
1714    // (We're no worse off than before regarding
1715    // `spare_capacity[written..]`) which remains not logically exposed.)
1716    // We (non-debug )asserted immediately above that `new_len` conforms
1717    // to the invariant that it must not exceed `vec.capacity()`.
1718    unsafe {
1719        vec.set_len(new_len);
1720    }
1721    // SAFETY: We trust that `ascii_valid_up_to` and
1722    // `convert_latin1_to_utf8` are correct and the `Vec` contains
1723    // valid UTF-8.
1724    Cow::Owned(unsafe { String::from_utf8_unchecked(vec) })
1725}
1726
1727/// If the input is valid UTF-8 representing only Unicode code points from
1728/// U+0000 to U+00FF, inclusive, converts the input into output that
1729/// represents the value of each code point as the unsigned byte value of
1730/// each output byte.
1731///
1732/// If the input does not fulfill the condition stated above, this function
1733/// panics if debug assertions are enabled (and fuzzing isn't) and otherwise
1734/// does something that is memory-safe without any promises about any
1735/// properties of the output. In particular, callers shouldn't assume the
1736/// output to be the same across crate versions or CPU architectures and
1737/// should not assume that non-ASCII input can't map to ASCII output.
1738///
1739/// Borrows if input is ASCII-only. Performs a single heap allocation
1740/// otherwise.
1741///
1742/// Only available if the `alloc` feature is enabled (enabled by default).
1743#[cfg(feature = "alloc")]
1744pub fn encode_latin1_lossy<'a>(string: &'a str) -> Cow<'a, [u8]> {
1745    let bytes = string.as_bytes();
1746    let up_to = ascii_valid_up_to(bytes);
1747    // >= makes later things optimize better than ==
1748    if up_to >= bytes.len() {
1749        debug_assert_eq!(up_to, bytes.len());
1750        return Cow::Borrowed(bytes);
1751    }
1752    let (head, tail) = bytes.split_at(up_to);
1753    let capacity = bytes.len();
1754    let mut vec = Vec::with_capacity(capacity);
1755    vec.extend_from_slice(head);
1756    let old_len = vec.len();
1757    let spare_capacity = crate::minimally_init(vec.spare_capacity_mut());
1758    debug_assert_eq!(old_len, up_to);
1759    let written = convert_utf8_to_latin1_lossy(tail, spare_capacity);
1760    debug_assert!(written <= spare_capacity.len());
1761    let new_len = old_len + written;
1762    assert!(new_len <= vec.capacity());
1763    // SAFETY: We trust that `convert_utf8_to_latin1_lossy` wrote to every
1764    // element of `spare_capacity[..written]`.
1765    // (We're no worse off than before regarding
1766    // `spare_capacity[written..]`) which remains not logically exposed.)
1767    // We (non-debug )asserted immediately above that `new_len` conforms
1768    // to the invariant that it must not exceed `vec.capacity()`.
1769    unsafe {
1770        vec.set_len(new_len);
1771    }
1772    Cow::Owned(vec)
1773}
1774
1775cfg_if! {
1776    if #[cfg(all(
1777        feature = "simd-accel",
1778        target_endian = "little",
1779    ))] {
1780        pub(crate) use crate::simd_funcs::validate_bmp_stride;
1781        pub(crate) use crate::simd_funcs::validate_latin1_str_stride;
1782
1783        #[inline(always)]
1784        #[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
1785        fn is_str_latin1_impl(buffer: &str) -> Option<usize> {
1786            let mut consumed = 0;
1787            let (strides, tail) = buffer.as_bytes().as_chunks::<STRIDE>();
1788            for stride in strides.iter() {
1789                if let Some(pos) = validate_latin1_str_stride(stride) {
1790                    return Some(consumed + pos);
1791                }
1792                consumed += STRIDE;
1793            }
1794            for slot in tail.iter() {
1795                if *slot > 0xC3 {
1796                    return Some(consumed);
1797                }
1798                consumed += 1;
1799            }
1800            None
1801        }
1802    } else {
1803        #[inline(always)]
1804        pub(crate) fn validate_bmp_stride(stride: &[u16; STRIDE]) -> Option<usize> {
1805            if (stride[0] & 0xF800 != 0xD800)
1806                && (stride[1] & 0xF800 != 0xD800)
1807                && (stride[2] & 0xF800 != 0xD800)
1808                && (stride[3] & 0xF800 != 0xD800)
1809                && (stride[4] & 0xF800 != 0xD800)
1810                && (stride[5] & 0xF800 != 0xD800)
1811                && (stride[6] & 0xF800 != 0xD800)
1812                && (stride[7] & 0xF800 != 0xD800)
1813                && (stride[8] & 0xF800 != 0xD800)
1814                && (stride[9] & 0xF800 != 0xD800)
1815                && (stride[10] & 0xF800 != 0xD800)
1816                && (stride[11] & 0xF800 != 0xD800)
1817                && (stride[12] & 0xF800 != 0xD800)
1818                && (stride[13] & 0xF800 != 0xD800)
1819                && (stride[14] & 0xF800 != 0xD800)
1820                && (stride[15] & 0xF800 != 0xD800)
1821            {
1822                return None;
1823            }
1824            for (i, c) in stride.iter().enumerate() {
1825                if c & 0xF800 == 0xD800 {
1826                    return Some(i);
1827                }
1828            }
1829            debug_assert!(false);
1830            None
1831        }
1832
1833        #[inline(always)]
1834        fn is_str_latin1_impl(buffer: &str) -> Option<usize> {
1835            let mut bytes = buffer.as_bytes();
1836            let mut total = 0;
1837            loop {
1838                if let Some((byte, offset)) = validate_ascii(bytes) {
1839                    total += offset;
1840                    if byte > 0xC3 {
1841                        return Some(total);
1842                    }
1843                    bytes = &bytes[offset + 2..];
1844                    total += 2;
1845                } else {
1846                    return None;
1847                }
1848            }
1849        }
1850    }
1851}
1852
1853/// Returns the index of the first unpaired surrogate or, if the input is
1854/// valid UTF-16 in its entirety, the length of the input.
1855#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
1856pub fn utf16_valid_up_to(buffer: &[u16]) -> usize {
1857    let mut consumed = 0usize;
1858    'outer: loop {
1859        let (strides, tail) = &buffer[consumed..].as_chunks::<STRIDE>();
1860        // This loop is only broken out of as goto forward.
1861        #[allow(clippy::never_loop)]
1862        'bmp: loop {
1863            for stride in strides.iter() {
1864                if let Some(pos) = validate_bmp_stride(stride) {
1865                    consumed += pos;
1866                    break 'bmp;
1867                }
1868                consumed += STRIDE;
1869            }
1870            for slot in tail.iter() {
1871                if *slot & 0xF800 == 0xD800 {
1872                    break 'bmp;
1873                }
1874                consumed += 1;
1875            }
1876            debug_assert_eq!(consumed, buffer.len());
1877            return consumed;
1878        }
1879        // `buffer[consumed]` is now in range and is a surrogate.
1880        let mut unit = buffer[consumed];
1881        let mut unit_minus_surrogate_start = unit.wrapping_sub(0xD800);
1882        debug_assert!(unit_minus_surrogate_start <= (0xDFFF - 0xD800));
1883        'surrogate: loop {
1884            if unit_minus_surrogate_start > (0xDBFF - 0xD800) {
1885                // Not high surrogate. Must be unpaired low surrogate.
1886                return consumed;
1887            }
1888            // high surrogate
1889            let next = consumed + 1;
1890            if next == buffer.len() {
1891                // Buffer ends with unpaired high surrogate
1892                return consumed;
1893            }
1894            let second = buffer[next];
1895            let second_minus_low_surrogate_start = second.wrapping_sub(0xDC00);
1896            if second_minus_low_surrogate_start > (0xDFFF - 0xDC00) {
1897                // The next unit is not a low surrogate. We had an unpaired.
1898                // high surrogate.
1899                return consumed;
1900            }
1901            // The next code unit is a low surrogate. Advance position.
1902            consumed = next + 1;
1903            // We could just do `continue 'outer;` here, and that would
1904            // be optimal for emoji and the occasional non-BMP Hanzi.
1905            // However, that would be bad for non-BMP scripts.
1906            loop {
1907                if consumed == buffer.len() {
1908                    // End of buffer.
1909                    return consumed;
1910                }
1911                unit = buffer[consumed];
1912                unit_minus_surrogate_start = unit.wrapping_sub(0xD800);
1913                if unit_minus_surrogate_start <= (0xDFFF - 0xD800) {
1914                    continue 'surrogate;
1915                }
1916                // We got a non-surrogate.
1917                consumed += 1;
1918                // Avoid bouncing to SIMD for ASCII spaces.
1919                if unit == 0x0020 {
1920                    continue;
1921                }
1922                continue 'outer;
1923            }
1924        }
1925    }
1926}
1927
1928/// Returns the index of first byte that starts an invalid byte
1929/// sequence or a non-Latin1 byte sequence, or the length of the
1930/// string if there are neither.
1931pub fn utf8_latin1_up_to(buffer: &[u8]) -> usize {
1932    is_utf8_latin1_impl(buffer).unwrap_or(buffer.len())
1933}
1934
1935/// Returns the index of first byte that starts a non-Latin1 byte
1936/// sequence, or the length of the string if there are none.
1937pub fn str_latin1_up_to(buffer: &str) -> usize {
1938    is_str_latin1_impl(buffer).unwrap_or(buffer.len())
1939}
1940
1941/// Replaces unpaired surrogates in the input with the REPLACEMENT CHARACTER.
1942#[inline]
1943pub fn ensure_utf16_validity(buffer: &mut [u16]) {
1944    let mut offset = 0;
1945    loop {
1946        offset += utf16_valid_up_to(&buffer[offset..]);
1947        if offset == buffer.len() {
1948            return;
1949        }
1950        buffer[offset] = 0xFFFD;
1951        offset += 1;
1952    }
1953}
1954
1955/// Copies ASCII from source to destination up to the first non-ASCII byte
1956/// (or the end of the input if it is ASCII in its entirety).
1957///
1958/// The length of the destination buffer must be at least the length of the
1959/// source buffer.
1960///
1961/// Returns the number of bytes written.
1962///
1963/// # Panics
1964///
1965/// Panics if the destination buffer is shorter than stated above.
1966pub fn copy_ascii_to_ascii(src: &[u8], dst: &mut [u8]) -> usize {
1967    assert!(
1968        dst.len() >= src.len(),
1969        "Destination must not be shorter than the source."
1970    );
1971    if let Some((_, consumed)) = { ascii_to_ascii(src, dst) } {
1972        consumed
1973    } else {
1974        src.len()
1975    }
1976}
1977
1978/// Copies ASCII from source to destination zero-extending it to UTF-16 up to
1979/// the first non-ASCII byte (or the end of the input if it is ASCII in its
1980/// entirety).
1981///
1982/// The length of the destination buffer must be at least the length of the
1983/// source buffer.
1984///
1985/// Returns the number of `u16`s written.
1986///
1987/// # Panics
1988///
1989/// Panics if the destination buffer is shorter than stated above.
1990#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
1991pub fn copy_ascii_to_basic_latin(src: &[u8], dst: &mut [u16]) -> usize {
1992    assert!(
1993        dst.len() >= src.len(),
1994        "Destination must not be shorter than the source."
1995    );
1996    if let Some((_, consumed)) = { ascii_to_basic_latin(src, dst) } {
1997        consumed
1998    } else {
1999        src.len()
2000    }
2001}
2002
2003/// Copies Basic Latin from source to destination narrowing it to ASCII up to
2004/// the first non-Basic Latin code unit (or the end of the input if it is
2005/// Basic Latin in its entirety).
2006///
2007/// The length of the destination buffer must be at least the length of the
2008/// source buffer.
2009///
2010/// Returns the number of bytes written.
2011///
2012/// # Panics
2013///
2014/// Panics if the destination buffer is shorter than stated above.
2015#[crate::multiversion(targets("x86_64+avx2+bmi1", "x86+avx2+bmi1"))]
2016pub fn copy_basic_latin_to_ascii(src: &[u16], dst: &mut [u8]) -> usize {
2017    assert!(
2018        dst.len() >= src.len(),
2019        "Destination must not be shorter than the source."
2020    );
2021    if let Some((_, consumed)) = { basic_latin_to_ascii(src, dst) } {
2022        consumed
2023    } else {
2024        src.len()
2025    }
2026}
2027
2028// Any copyright to the test code below this comment is dedicated to the
2029// Public Domain. http://creativecommons.org/publicdomain/zero/1.0/
2030
2031#[cfg(all(test, feature = "alloc"))]
2032mod tests {
2033    use super::*;
2034
2035    #[test]
2036    fn test_is_ascii_success() {
2037        let mut src: Vec<u8> = Vec::with_capacity(128);
2038        src.resize(128, 0);
2039        for i in 0..src.len() {
2040            src[i] = i as u8;
2041        }
2042        for i in 0..src.len() {
2043            assert!(is_ascii(&src[i..]));
2044        }
2045    }
2046
2047    #[test]
2048    fn test_is_ascii_fail() {
2049        let mut src: Vec<u8> = Vec::with_capacity(128);
2050        src.resize(128, 0);
2051        for i in 0..src.len() {
2052            src[i] = i as u8;
2053        }
2054        for i in 0..src.len() {
2055            let tail = &mut src[i..];
2056            for j in 0..tail.len() {
2057                tail[j] = 0xA0;
2058                assert!(!is_ascii(tail));
2059            }
2060        }
2061    }
2062
2063    #[test]
2064    fn test_is_basic_latin_success() {
2065        let mut src: Vec<u16> = Vec::with_capacity(128);
2066        src.resize(128, 0);
2067        for i in 0..src.len() {
2068            src[i] = i as u16;
2069        }
2070        for i in 0..src.len() {
2071            assert!(is_basic_latin(&src[i..]));
2072        }
2073    }
2074
2075    #[test]
2076    fn test_is_basic_latin_fail() {
2077        let mut src: Vec<u16> = Vec::with_capacity(128);
2078        src.resize(128, 0);
2079        for i in 0..src.len() {
2080            src[i] = i as u16;
2081        }
2082        for i in 0..src.len() {
2083            let tail = &mut src[i..];
2084            for j in 0..tail.len() {
2085                tail[j] = 0xA0;
2086                assert!(!is_basic_latin(tail));
2087            }
2088        }
2089    }
2090
2091    #[test]
2092    fn test_is_utf16_latin1_success() {
2093        let mut src: Vec<u16> = Vec::with_capacity(256);
2094        src.resize(256, 0);
2095        for i in 0..src.len() {
2096            src[i] = i as u16;
2097        }
2098        for i in 0..src.len() {
2099            assert!(is_utf16_latin1(&src[i..]));
2100            assert_eq!(
2101                check_utf16_for_latin1_and_bidi(&src[i..]),
2102                Latin1Bidi::Latin1
2103            );
2104        }
2105    }
2106
2107    #[test]
2108    fn test_is_utf16_latin1_fail() {
2109        let len = if cfg!(miri) { 64 } else { 256 }; // Miri is too slow
2110        let mut src: Vec<u16> = Vec::with_capacity(len);
2111        src.resize(len, 0);
2112        for i in 0..src.len() {
2113            src[i] = i as u16;
2114        }
2115        for i in 0..src.len() {
2116            let tail = &mut src[i..];
2117            for j in 0..tail.len() {
2118                tail[j] = 0x100 + j as u16;
2119                assert!(!is_utf16_latin1(tail));
2120                assert_ne!(check_utf16_for_latin1_and_bidi(tail), Latin1Bidi::Latin1);
2121            }
2122        }
2123    }
2124
2125    #[test]
2126    fn test_is_str_latin1_success() {
2127        let len = if cfg!(miri) { 64 } else { 256 }; // Miri is too slow
2128        let mut src: Vec<u16> = Vec::with_capacity(len);
2129        src.resize(len, 0);
2130        for i in 0..src.len() {
2131            src[i] = i as u16;
2132        }
2133        for i in 0..src.len() {
2134            let s = String::from_utf16(&src[i..]).unwrap();
2135            assert!(is_str_latin1(&s[..]));
2136            assert_eq!(check_str_for_latin1_and_bidi(&s[..]), Latin1Bidi::Latin1);
2137        }
2138    }
2139
2140    #[test]
2141    fn test_is_str_latin1_fail() {
2142        let len = if cfg!(miri) { 32 } else { 256 }; // Miri is too slow
2143        let mut src: Vec<u16> = Vec::with_capacity(len);
2144        src.resize(len, 0);
2145        for i in 0..src.len() {
2146            src[i] = i as u16;
2147        }
2148        for i in 0..src.len() {
2149            let tail = &mut src[i..];
2150            for j in 0..tail.len() {
2151                tail[j] = 0x100 + j as u16;
2152                let s = String::from_utf16(tail).unwrap();
2153                assert!(!is_str_latin1(&s[..]));
2154                assert_ne!(check_str_for_latin1_and_bidi(&s[..]), Latin1Bidi::Latin1);
2155            }
2156        }
2157    }
2158
2159    #[test]
2160    fn test_is_utf8_latin1_success() {
2161        let len = if cfg!(miri) { 64 } else { 256 }; // Miri is too slow
2162        let mut src: Vec<u16> = Vec::with_capacity(len);
2163        src.resize(len, 0);
2164        for i in 0..src.len() {
2165            src[i] = i as u16;
2166        }
2167        for i in 0..src.len() {
2168            let s = String::from_utf16(&src[i..]).unwrap();
2169            assert!(is_utf8_latin1(s.as_bytes()));
2170            assert_eq!(
2171                check_utf8_for_latin1_and_bidi(s.as_bytes()),
2172                Latin1Bidi::Latin1
2173            );
2174        }
2175    }
2176
2177    #[test]
2178    fn test_is_utf8_latin1_fail() {
2179        let len = if cfg!(miri) { 32 } else { 256 }; // Miri is too slow
2180        let mut src: Vec<u16> = Vec::with_capacity(len);
2181        src.resize(len, 0);
2182        for i in 0..src.len() {
2183            src[i] = i as u16;
2184        }
2185        for i in 0..src.len() {
2186            let tail = &mut src[i..];
2187            for j in 0..tail.len() {
2188                tail[j] = 0x100 + j as u16;
2189                let s = String::from_utf16(tail).unwrap();
2190                assert!(!is_utf8_latin1(s.as_bytes()));
2191                assert_ne!(
2192                    check_utf8_for_latin1_and_bidi(s.as_bytes()),
2193                    Latin1Bidi::Latin1
2194                );
2195            }
2196        }
2197    }
2198
2199    #[test]
2200    fn test_is_utf8_latin1_invalid() {
2201        assert!(!is_utf8_latin1(b"\xC3"));
2202        assert!(!is_utf8_latin1(b"a\xC3"));
2203        assert!(!is_utf8_latin1(b"\xFF"));
2204        assert!(!is_utf8_latin1(b"a\xFF"));
2205        assert!(!is_utf8_latin1(b"\xC3\xFF"));
2206        assert!(!is_utf8_latin1(b"a\xC3\xFF"));
2207    }
2208
2209    #[test]
2210    fn test_convert_utf8_to_utf16() {
2211        let src = "abcdefghijklmnopqrstu\u{1F4A9}v\u{2603}w\u{00B6}xyzz";
2212        let mut dst: Vec<u16> = Vec::with_capacity(src.len() + 1);
2213        dst.resize(src.len() + 1, 0);
2214        let len = convert_utf8_to_utf16(src.as_bytes(), &mut dst[..]);
2215        dst.truncate(len);
2216        let reference: Vec<u16> = src.encode_utf16().collect();
2217        assert_eq!(dst, reference);
2218    }
2219
2220    #[test]
2221    fn test_convert_str_to_utf16() {
2222        let src = "abcdefghijklmnopqrstu\u{1F4A9}v\u{2603}w\u{00B6}xyzz";
2223        let mut dst: Vec<u16> = Vec::with_capacity(src.len());
2224        dst.resize(src.len(), 0);
2225        let len = convert_str_to_utf16(src, &mut dst[..]);
2226        dst.truncate(len);
2227        let reference: Vec<u16> = src.encode_utf16().collect();
2228        assert_eq!(dst, reference);
2229    }
2230
2231    #[test]
2232    fn test_convert_utf16_to_utf8_partial() {
2233        let reference = "abcdefghijklmnopqrstu\u{1F4A9}v\u{2603}w\u{00B6}xyzz";
2234        let src: Vec<u16> = reference.encode_utf16().collect();
2235        let mut dst: Vec<u8> = Vec::with_capacity(src.len() * 3 + 1);
2236        dst.resize(src.len() * 3 + 1, 0);
2237        let (read, written) = convert_utf16_to_utf8_partial(&src[..], &mut dst[..24]);
2238        let len = written + convert_utf16_to_utf8(&src[read..], &mut dst[written..]);
2239        dst.truncate(len);
2240        assert_eq!(dst, reference.as_bytes());
2241    }
2242
2243    #[test]
2244    fn test_convert_utf16_to_utf8() {
2245        let reference = "abcdefghijklmnopqrstu\u{1F4A9}v\u{2603}w\u{00B6}xyzz";
2246        let src: Vec<u16> = reference.encode_utf16().collect();
2247        let mut dst: Vec<u8> = Vec::with_capacity(src.len() * 3 + 1);
2248        dst.resize(src.len() * 3 + 1, 0);
2249        let len = convert_utf16_to_utf8(&src[..], &mut dst[..]);
2250        dst.truncate(len);
2251        assert_eq!(dst, reference.as_bytes());
2252    }
2253
2254    #[test]
2255    fn test_convert_latin1_to_utf16() {
2256        let mut src: Vec<u8> = Vec::with_capacity(256);
2257        src.resize(256, 0);
2258        let mut reference: Vec<u16> = Vec::with_capacity(256);
2259        reference.resize(256, 0);
2260        for i in 0..256 {
2261            src[i] = i as u8;
2262            reference[i] = i as u16;
2263        }
2264        let mut dst: Vec<u16> = Vec::with_capacity(src.len());
2265        dst.resize(src.len(), 0);
2266        convert_latin1_to_utf16(&src[..], &mut dst[..]);
2267        assert_eq!(dst, reference);
2268    }
2269
2270    #[test]
2271    fn test_convert_latin1_to_utf8_partial() {
2272        let mut dst = [0u8, 2];
2273        let (read, written) = convert_latin1_to_utf8_partial(b"a\xFF", &mut dst[..]);
2274        assert_eq!(read, 1);
2275        assert_eq!(written, 1);
2276    }
2277
2278    #[test]
2279    fn test_convert_latin1_to_utf8() {
2280        let mut src: Vec<u8> = Vec::with_capacity(256);
2281        src.resize(256, 0);
2282        let mut reference: Vec<u16> = Vec::with_capacity(256);
2283        reference.resize(256, 0);
2284        for i in 0..256 {
2285            src[i] = i as u8;
2286            reference[i] = i as u16;
2287        }
2288        let s = String::from_utf16(&reference[..]).unwrap();
2289        let mut dst: Vec<u8> = Vec::with_capacity(src.len() * 2);
2290        dst.resize(src.len() * 2, 0);
2291        let len = convert_latin1_to_utf8(&src[..], &mut dst[..]);
2292        dst.truncate(len);
2293        assert_eq!(&dst[..], s.as_bytes());
2294    }
2295
2296    #[test]
2297    fn test_convert_utf8_to_latin1_lossy() {
2298        let mut reference: Vec<u8> = Vec::with_capacity(256);
2299        reference.resize(256, 0);
2300        let mut src16: Vec<u16> = Vec::with_capacity(256);
2301        src16.resize(256, 0);
2302        for i in 0..256 {
2303            src16[i] = i as u16;
2304            reference[i] = i as u8;
2305        }
2306        let src = String::from_utf16(&src16[..]).unwrap();
2307        let mut dst: Vec<u8> = Vec::with_capacity(src.len());
2308        dst.resize(src.len(), 0);
2309        let len = convert_utf8_to_latin1_lossy(src.as_bytes(), &mut dst[..]);
2310        dst.truncate(len);
2311        assert_eq!(dst, reference);
2312    }
2313
2314    #[cfg(all(debug_assertions, not(fuzzing)))]
2315    #[test]
2316    #[should_panic]
2317    fn test_convert_utf8_to_latin1_lossy_panics() {
2318        let mut dst = [0u8; 16];
2319        let _ = convert_utf8_to_latin1_lossy("\u{100}".as_bytes(), &mut dst[..]);
2320    }
2321
2322    #[test]
2323    fn test_convert_utf16_to_latin1_lossy() {
2324        let mut src: Vec<u16> = Vec::with_capacity(256);
2325        src.resize(256, 0);
2326        let mut reference: Vec<u8> = Vec::with_capacity(256);
2327        reference.resize(256, 0);
2328        for i in 0..256 {
2329            src[i] = i as u16;
2330            reference[i] = i as u8;
2331        }
2332        let mut dst: Vec<u8> = Vec::with_capacity(src.len());
2333        dst.resize(src.len(), 0);
2334        convert_utf16_to_latin1_lossy(&src[..], &mut dst[..]);
2335        assert_eq!(dst, reference);
2336    }
2337
2338    #[test]
2339    // #[should_panic]
2340    fn test_convert_utf16_to_latin1_lossy_panics() {
2341        let mut dst = [0u8; 16];
2342        let _ = convert_utf16_to_latin1_lossy(&[0x0100u16], &mut dst[..]);
2343    }
2344
2345    #[test]
2346    fn test_utf16_valid_up_to() {
2347        let valid = vec![
2348            0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0x2603u16,
2349            0xD83Du16, 0xDCA9u16, 0x00B6u16,
2350        ];
2351        assert_eq!(utf16_valid_up_to(&valid[..]), 16);
2352        let lone_high = vec![
2353            0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2354            0x2603u16, 0xD83Du16, 0x00B6u16,
2355        ];
2356        assert_eq!(utf16_valid_up_to(&lone_high[..]), 14);
2357        let lone_low = vec![
2358            0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2359            0x2603u16, 0xDCA9u16, 0x00B6u16,
2360        ];
2361        assert_eq!(utf16_valid_up_to(&lone_low[..]), 14);
2362        let lone_high_at_end = vec![
2363            0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2364            0x2603u16, 0x00B6u16, 0xD83Du16,
2365        ];
2366        assert_eq!(utf16_valid_up_to(&lone_high_at_end[..]), 15);
2367    }
2368
2369    #[test]
2370    fn test_ensure_utf16_validity() {
2371        let mut src = vec![
2372            0u16, 0xD83Du16, 0u16, 0u16, 0u16, 0xD83Du16, 0xDCA9u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2373            0u16, 0xDCA9u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2374            0u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2375        ];
2376        let reference = vec![
2377            0u16, 0xFFFDu16, 0u16, 0u16, 0u16, 0xD83Du16, 0xDCA9u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2378            0u16, 0xFFFDu16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2379            0u16, 0u16, 0u16, 0u16, 0u16, 0u16,
2380        ];
2381        ensure_utf16_validity(&mut src[..]);
2382        assert_eq!(src, reference);
2383    }
2384
2385    #[test]
2386    fn test_is_char_bidi() {
2387        assert!(!is_char_bidi('a'));
2388        assert!(!is_char_bidi('\u{03B1}'));
2389        assert!(!is_char_bidi('\u{3041}'));
2390        assert!(!is_char_bidi('\u{1F4A9}'));
2391        assert!(!is_char_bidi('\u{FE00}'));
2392        assert!(!is_char_bidi('\u{202C}'));
2393        assert!(!is_char_bidi('\u{FEFF}'));
2394        assert!(is_char_bidi('\u{0590}'));
2395        assert!(is_char_bidi('\u{08FF}'));
2396        assert!(is_char_bidi('\u{061C}'));
2397        assert!(is_char_bidi('\u{FB50}'));
2398        assert!(is_char_bidi('\u{FDFF}'));
2399        assert!(is_char_bidi('\u{FE70}'));
2400        assert!(is_char_bidi('\u{FEFE}'));
2401        assert!(is_char_bidi('\u{200F}'));
2402        assert!(is_char_bidi('\u{202B}'));
2403        assert!(is_char_bidi('\u{202E}'));
2404        assert!(is_char_bidi('\u{2067}'));
2405        assert!(is_char_bidi('\u{10800}'));
2406        assert!(is_char_bidi('\u{10FFF}'));
2407        assert!(is_char_bidi('\u{1E800}'));
2408        assert!(is_char_bidi('\u{1EFFF}'));
2409    }
2410
2411    #[test]
2412    fn test_is_utf16_code_unit_bidi() {
2413        assert!(!is_utf16_code_unit_bidi(0x0062));
2414        assert!(!is_utf16_code_unit_bidi(0x03B1));
2415        assert!(!is_utf16_code_unit_bidi(0x3041));
2416        assert!(!is_utf16_code_unit_bidi(0xD801));
2417        assert!(!is_utf16_code_unit_bidi(0xFE00));
2418        assert!(!is_utf16_code_unit_bidi(0x202C));
2419        assert!(!is_utf16_code_unit_bidi(0xFEFF));
2420        assert!(is_utf16_code_unit_bidi(0x0590));
2421        assert!(is_utf16_code_unit_bidi(0x08FF));
2422        assert!(is_utf16_code_unit_bidi(0x061C));
2423        assert!(is_utf16_code_unit_bidi(0xFB1D));
2424        assert!(is_utf16_code_unit_bidi(0xFB50));
2425        assert!(is_utf16_code_unit_bidi(0xFDFF));
2426        assert!(is_utf16_code_unit_bidi(0xFE70));
2427        assert!(is_utf16_code_unit_bidi(0xFEFE));
2428        assert!(is_utf16_code_unit_bidi(0x200F));
2429        assert!(is_utf16_code_unit_bidi(0x202B));
2430        assert!(is_utf16_code_unit_bidi(0x202E));
2431        assert!(is_utf16_code_unit_bidi(0x2067));
2432        assert!(is_utf16_code_unit_bidi(0xD802));
2433        assert!(is_utf16_code_unit_bidi(0xD803));
2434        assert!(is_utf16_code_unit_bidi(0xD83A));
2435        assert!(is_utf16_code_unit_bidi(0xD83B));
2436    }
2437
2438    #[test]
2439    fn test_is_str_bidi() {
2440        assert!(!is_str_bidi("abcdefghijklmnopaabcdefghijklmnop"));
2441        assert!(!is_str_bidi("abcdefghijklmnop\u{03B1}abcdefghijklmnop"));
2442        assert!(!is_str_bidi("abcdefghijklmnop\u{3041}abcdefghijklmnop"));
2443        assert!(!is_str_bidi("abcdefghijklmnop\u{1F4A9}abcdefghijklmnop"));
2444        assert!(!is_str_bidi("abcdefghijklmnop\u{FE00}abcdefghijklmnop"));
2445        assert!(!is_str_bidi("abcdefghijklmnop\u{202C}abcdefghijklmnop"));
2446        assert!(!is_str_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop"));
2447        assert!(is_str_bidi("abcdefghijklmnop\u{0590}abcdefghijklmnop"));
2448        assert!(is_str_bidi("abcdefghijklmnop\u{08FF}abcdefghijklmnop"));
2449        assert!(is_str_bidi("abcdefghijklmnop\u{061C}abcdefghijklmnop"));
2450        assert!(is_str_bidi("abcdefghijklmnop\u{FB50}abcdefghijklmnop"));
2451        assert!(is_str_bidi("abcdefghijklmnop\u{FDFF}abcdefghijklmnop"));
2452        assert!(is_str_bidi("abcdefghijklmnop\u{FE70}abcdefghijklmnop"));
2453        assert!(is_str_bidi("abcdefghijklmnop\u{FEFE}abcdefghijklmnop"));
2454        assert!(is_str_bidi("abcdefghijklmnop\u{200F}abcdefghijklmnop"));
2455        assert!(is_str_bidi("abcdefghijklmnop\u{202B}abcdefghijklmnop"));
2456        assert!(is_str_bidi("abcdefghijklmnop\u{202E}abcdefghijklmnop"));
2457        assert!(is_str_bidi("abcdefghijklmnop\u{2067}abcdefghijklmnop"));
2458        assert!(is_str_bidi("abcdefghijklmnop\u{10800}abcdefghijklmnop"));
2459        assert!(is_str_bidi("abcdefghijklmnop\u{10FFF}abcdefghijklmnop"));
2460        assert!(is_str_bidi("abcdefghijklmnop\u{1E800}abcdefghijklmnop"));
2461        assert!(is_str_bidi("abcdefghijklmnop\u{1EFFF}abcdefghijklmnop"));
2462    }
2463
2464    #[test]
2465    fn test_is_utf8_bidi() {
2466        assert!(!is_utf8_bidi(
2467            "abcdefghijklmnopaabcdefghijklmnop".as_bytes()
2468        ));
2469        assert!(!is_utf8_bidi(
2470            "abcdefghijklmnop\u{03B1}abcdefghijklmnop".as_bytes()
2471        ));
2472        assert!(!is_utf8_bidi(
2473            "abcdefghijklmnop\u{3041}abcdefghijklmnop".as_bytes()
2474        ));
2475        assert!(!is_utf8_bidi(
2476            "abcdefghijklmnop\u{1F4A9}abcdefghijklmnop".as_bytes()
2477        ));
2478        assert!(!is_utf8_bidi(
2479            "abcdefghijklmnop\u{FE00}abcdefghijklmnop".as_bytes()
2480        ));
2481        assert!(!is_utf8_bidi(
2482            "abcdefghijklmnop\u{202C}abcdefghijklmnop".as_bytes()
2483        ));
2484        assert!(!is_utf8_bidi(
2485            "abcdefghijklmnop\u{FEFF}abcdefghijklmnop".as_bytes()
2486        ));
2487        assert!(is_utf8_bidi(
2488            "abcdefghijklmnop\u{0590}abcdefghijklmnop".as_bytes()
2489        ));
2490        assert!(is_utf8_bidi(
2491            "abcdefghijklmnop\u{08FF}abcdefghijklmnop".as_bytes()
2492        ));
2493        assert!(is_utf8_bidi(
2494            "abcdefghijklmnop\u{061C}abcdefghijklmnop".as_bytes()
2495        ));
2496        assert!(is_utf8_bidi(
2497            "abcdefghijklmnop\u{FB50}abcdefghijklmnop".as_bytes()
2498        ));
2499        assert!(is_utf8_bidi(
2500            "abcdefghijklmnop\u{FDFF}abcdefghijklmnop".as_bytes()
2501        ));
2502        assert!(is_utf8_bidi(
2503            "abcdefghijklmnop\u{FE70}abcdefghijklmnop".as_bytes()
2504        ));
2505        assert!(is_utf8_bidi(
2506            "abcdefghijklmnop\u{FEFE}abcdefghijklmnop".as_bytes()
2507        ));
2508        assert!(is_utf8_bidi(
2509            "abcdefghijklmnop\u{200F}abcdefghijklmnop".as_bytes()
2510        ));
2511        assert!(is_utf8_bidi(
2512            "abcdefghijklmnop\u{202B}abcdefghijklmnop".as_bytes()
2513        ));
2514        assert!(is_utf8_bidi(
2515            "abcdefghijklmnop\u{202E}abcdefghijklmnop".as_bytes()
2516        ));
2517        assert!(is_utf8_bidi(
2518            "abcdefghijklmnop\u{2067}abcdefghijklmnop".as_bytes()
2519        ));
2520        assert!(is_utf8_bidi(
2521            "abcdefghijklmnop\u{10800}abcdefghijklmnop".as_bytes()
2522        ));
2523        assert!(is_utf8_bidi(
2524            "abcdefghijklmnop\u{10FFF}abcdefghijklmnop".as_bytes()
2525        ));
2526        assert!(is_utf8_bidi(
2527            "abcdefghijklmnop\u{1E800}abcdefghijklmnop".as_bytes()
2528        ));
2529        assert!(is_utf8_bidi(
2530            "abcdefghijklmnop\u{1EFFF}abcdefghijklmnop".as_bytes()
2531        ));
2532    }
2533
2534    #[test]
2535    fn test_is_utf16_bidi() {
2536        assert!(!is_utf16_bidi(&[
2537            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x0062, 0x62, 0x63, 0x64, 0x65, 0x66,
2538            0x67, 0x68, 0x69,
2539        ]));
2540        assert!(!is_utf16_bidi(&[
2541            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x03B1, 0x62, 0x63, 0x64, 0x65, 0x66,
2542            0x67, 0x68, 0x69,
2543        ]));
2544        assert!(!is_utf16_bidi(&[
2545            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x3041, 0x62, 0x63, 0x64, 0x65, 0x66,
2546            0x67, 0x68, 0x69,
2547        ]));
2548        assert!(!is_utf16_bidi(&[
2549            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD801, 0x62, 0x63, 0x64, 0x65, 0x66,
2550            0x67, 0x68, 0x69,
2551        ]));
2552        assert!(!is_utf16_bidi(&[
2553            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFE00, 0x62, 0x63, 0x64, 0x65, 0x66,
2554            0x67, 0x68, 0x69,
2555        ]));
2556        assert!(!is_utf16_bidi(&[
2557            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202C, 0x62, 0x63, 0x64, 0x65, 0x66,
2558            0x67, 0x68, 0x69,
2559        ]));
2560        assert!(!is_utf16_bidi(&[
2561            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFF, 0x62, 0x63, 0x64, 0x65, 0x66,
2562            0x67, 0x68, 0x69,
2563        ]));
2564        assert!(is_utf16_bidi(&[
2565            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x0590, 0x62, 0x63, 0x64, 0x65, 0x66,
2566            0x67, 0x68, 0x69,
2567        ]));
2568        assert!(is_utf16_bidi(&[
2569            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x08FF, 0x62, 0x63, 0x64, 0x65, 0x66,
2570            0x67, 0x68, 0x69,
2571        ]));
2572        assert!(is_utf16_bidi(&[
2573            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x061C, 0x62, 0x63, 0x64, 0x65, 0x66,
2574            0x67, 0x68, 0x69,
2575        ]));
2576        assert!(is_utf16_bidi(&[
2577            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFB1D, 0x62, 0x63, 0x64, 0x65, 0x66,
2578            0x67, 0x68, 0x69,
2579        ]));
2580        assert!(is_utf16_bidi(&[
2581            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFB50, 0x62, 0x63, 0x64, 0x65, 0x66,
2582            0x67, 0x68, 0x69,
2583        ]));
2584        assert!(is_utf16_bidi(&[
2585            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFDFF, 0x62, 0x63, 0x64, 0x65, 0x66,
2586            0x67, 0x68, 0x69,
2587        ]));
2588        assert!(is_utf16_bidi(&[
2589            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFE70, 0x62, 0x63, 0x64, 0x65, 0x66,
2590            0x67, 0x68, 0x69,
2591        ]));
2592        assert!(is_utf16_bidi(&[
2593            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFE, 0x62, 0x63, 0x64, 0x65, 0x66,
2594            0x67, 0x68, 0x69,
2595        ]));
2596        assert!(is_utf16_bidi(&[
2597            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x200F, 0x62, 0x63, 0x64, 0x65, 0x66,
2598            0x67, 0x68, 0x69,
2599        ]));
2600        assert!(is_utf16_bidi(&[
2601            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202B, 0x62, 0x63, 0x64, 0x65, 0x66,
2602            0x67, 0x68, 0x69,
2603        ]));
2604        assert!(is_utf16_bidi(&[
2605            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202E, 0x62, 0x63, 0x64, 0x65, 0x66,
2606            0x67, 0x68, 0x69,
2607        ]));
2608        assert!(is_utf16_bidi(&[
2609            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x2067, 0x62, 0x63, 0x64, 0x65, 0x66,
2610            0x67, 0x68, 0x69,
2611        ]));
2612        assert!(is_utf16_bidi(&[
2613            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD802, 0x62, 0x63, 0x64, 0x65, 0x66,
2614            0x67, 0x68, 0x69,
2615        ]));
2616        assert!(is_utf16_bidi(&[
2617            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD803, 0x62, 0x63, 0x64, 0x65, 0x66,
2618            0x67, 0x68, 0x69,
2619        ]));
2620        assert!(is_utf16_bidi(&[
2621            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD83A, 0x62, 0x63, 0x64, 0x65, 0x66,
2622            0x67, 0x68, 0x69,
2623        ]));
2624        assert!(is_utf16_bidi(&[
2625            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD83B, 0x62, 0x63, 0x64, 0x65, 0x66,
2626            0x67, 0x68, 0x69,
2627        ]));
2628
2629        assert!(is_utf16_bidi(&[
2630            0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x0590, 0x3041, 0x62, 0x63, 0x64, 0x65,
2631            0x66, 0x67, 0x68, 0x69,
2632        ]));
2633    }
2634
2635    #[test]
2636    fn test_check_str_for_latin1_and_bidi() {
2637        assert_ne!(
2638            check_str_for_latin1_and_bidi("abcdefghijklmnopaabcdefghijklmnop"),
2639            Latin1Bidi::Bidi
2640        );
2641        assert_ne!(
2642            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{03B1}abcdefghijklmnop"),
2643            Latin1Bidi::Bidi
2644        );
2645        assert_ne!(
2646            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{3041}abcdefghijklmnop"),
2647            Latin1Bidi::Bidi
2648        );
2649        assert_ne!(
2650            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{1F4A9}abcdefghijklmnop"),
2651            Latin1Bidi::Bidi
2652        );
2653        assert_ne!(
2654            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FE00}abcdefghijklmnop"),
2655            Latin1Bidi::Bidi
2656        );
2657        assert_ne!(
2658            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{202C}abcdefghijklmnop"),
2659            Latin1Bidi::Bidi
2660        );
2661        assert_ne!(
2662            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop"),
2663            Latin1Bidi::Bidi
2664        );
2665        assert_eq!(
2666            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{0590}abcdefghijklmnop"),
2667            Latin1Bidi::Bidi
2668        );
2669        assert_eq!(
2670            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{08FF}abcdefghijklmnop"),
2671            Latin1Bidi::Bidi
2672        );
2673        assert_eq!(
2674            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{061C}abcdefghijklmnop"),
2675            Latin1Bidi::Bidi
2676        );
2677        assert_eq!(
2678            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FB50}abcdefghijklmnop"),
2679            Latin1Bidi::Bidi
2680        );
2681        assert_eq!(
2682            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FDFF}abcdefghijklmnop"),
2683            Latin1Bidi::Bidi
2684        );
2685        assert_eq!(
2686            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FE70}abcdefghijklmnop"),
2687            Latin1Bidi::Bidi
2688        );
2689        assert_eq!(
2690            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{FEFE}abcdefghijklmnop"),
2691            Latin1Bidi::Bidi
2692        );
2693        assert_eq!(
2694            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{200F}abcdefghijklmnop"),
2695            Latin1Bidi::Bidi
2696        );
2697        assert_eq!(
2698            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{202B}abcdefghijklmnop"),
2699            Latin1Bidi::Bidi
2700        );
2701        assert_eq!(
2702            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{202E}abcdefghijklmnop"),
2703            Latin1Bidi::Bidi
2704        );
2705        assert_eq!(
2706            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{2067}abcdefghijklmnop"),
2707            Latin1Bidi::Bidi
2708        );
2709        assert_eq!(
2710            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{10800}abcdefghijklmnop"),
2711            Latin1Bidi::Bidi
2712        );
2713        assert_eq!(
2714            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{10FFF}abcdefghijklmnop"),
2715            Latin1Bidi::Bidi
2716        );
2717        assert_eq!(
2718            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{1E800}abcdefghijklmnop"),
2719            Latin1Bidi::Bidi
2720        );
2721        assert_eq!(
2722            check_str_for_latin1_and_bidi("abcdefghijklmnop\u{1EFFF}abcdefghijklmnop"),
2723            Latin1Bidi::Bidi
2724        );
2725    }
2726
2727    #[test]
2728    fn test_check_utf8_for_latin1_and_bidi() {
2729        assert_ne!(
2730            check_utf8_for_latin1_and_bidi("abcdefghijklmnopaabcdefghijklmnop".as_bytes()),
2731            Latin1Bidi::Bidi
2732        );
2733        assert_ne!(
2734            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{03B1}abcdefghijklmnop".as_bytes()),
2735            Latin1Bidi::Bidi
2736        );
2737        assert_ne!(
2738            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{3041}abcdefghijklmnop".as_bytes()),
2739            Latin1Bidi::Bidi
2740        );
2741        assert_ne!(
2742            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{1F4A9}abcdefghijklmnop".as_bytes()),
2743            Latin1Bidi::Bidi
2744        );
2745        assert_ne!(
2746            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FE00}abcdefghijklmnop".as_bytes()),
2747            Latin1Bidi::Bidi
2748        );
2749        assert_ne!(
2750            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{202C}abcdefghijklmnop".as_bytes()),
2751            Latin1Bidi::Bidi
2752        );
2753        assert_ne!(
2754            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FEFF}abcdefghijklmnop".as_bytes()),
2755            Latin1Bidi::Bidi
2756        );
2757        assert_eq!(
2758            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{0590}abcdefghijklmnop".as_bytes()),
2759            Latin1Bidi::Bidi
2760        );
2761        assert_eq!(
2762            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{08FF}abcdefghijklmnop".as_bytes()),
2763            Latin1Bidi::Bidi
2764        );
2765        assert_eq!(
2766            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{061C}abcdefghijklmnop".as_bytes()),
2767            Latin1Bidi::Bidi
2768        );
2769        assert_eq!(
2770            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FB50}abcdefghijklmnop".as_bytes()),
2771            Latin1Bidi::Bidi
2772        );
2773        assert_eq!(
2774            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FDFF}abcdefghijklmnop".as_bytes()),
2775            Latin1Bidi::Bidi
2776        );
2777        assert_eq!(
2778            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FE70}abcdefghijklmnop".as_bytes()),
2779            Latin1Bidi::Bidi
2780        );
2781        assert_eq!(
2782            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{FEFE}abcdefghijklmnop".as_bytes()),
2783            Latin1Bidi::Bidi
2784        );
2785        assert_eq!(
2786            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{200F}abcdefghijklmnop".as_bytes()),
2787            Latin1Bidi::Bidi
2788        );
2789        assert_eq!(
2790            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{202B}abcdefghijklmnop".as_bytes()),
2791            Latin1Bidi::Bidi
2792        );
2793        assert_eq!(
2794            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{202E}abcdefghijklmnop".as_bytes()),
2795            Latin1Bidi::Bidi
2796        );
2797        assert_eq!(
2798            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{2067}abcdefghijklmnop".as_bytes()),
2799            Latin1Bidi::Bidi
2800        );
2801        assert_eq!(
2802            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{10800}abcdefghijklmnop".as_bytes()),
2803            Latin1Bidi::Bidi
2804        );
2805        assert_eq!(
2806            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{10FFF}abcdefghijklmnop".as_bytes()),
2807            Latin1Bidi::Bidi
2808        );
2809        assert_eq!(
2810            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{1E800}abcdefghijklmnop".as_bytes()),
2811            Latin1Bidi::Bidi
2812        );
2813        assert_eq!(
2814            check_utf8_for_latin1_and_bidi("abcdefghijklmnop\u{1EFFF}abcdefghijklmnop".as_bytes()),
2815            Latin1Bidi::Bidi
2816        );
2817    }
2818
2819    #[test]
2820    fn test_check_utf16_for_latin1_and_bidi() {
2821        assert_ne!(
2822            check_utf16_for_latin1_and_bidi(&[
2823                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x0062, 0x62, 0x63, 0x64, 0x65,
2824                0x66, 0x67, 0x68, 0x69,
2825            ]),
2826            Latin1Bidi::Bidi
2827        );
2828        assert_ne!(
2829            check_utf16_for_latin1_and_bidi(&[
2830                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x03B1, 0x62, 0x63, 0x64, 0x65,
2831                0x66, 0x67, 0x68, 0x69,
2832            ]),
2833            Latin1Bidi::Bidi
2834        );
2835        assert_ne!(
2836            check_utf16_for_latin1_and_bidi(&[
2837                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x3041, 0x62, 0x63, 0x64, 0x65,
2838                0x66, 0x67, 0x68, 0x69,
2839            ]),
2840            Latin1Bidi::Bidi
2841        );
2842        assert_ne!(
2843            check_utf16_for_latin1_and_bidi(&[
2844                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD801, 0x62, 0x63, 0x64, 0x65,
2845                0x66, 0x67, 0x68, 0x69,
2846            ]),
2847            Latin1Bidi::Bidi
2848        );
2849        assert_ne!(
2850            check_utf16_for_latin1_and_bidi(&[
2851                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFE00, 0x62, 0x63, 0x64, 0x65,
2852                0x66, 0x67, 0x68, 0x69,
2853            ]),
2854            Latin1Bidi::Bidi
2855        );
2856        assert_ne!(
2857            check_utf16_for_latin1_and_bidi(&[
2858                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202C, 0x62, 0x63, 0x64, 0x65,
2859                0x66, 0x67, 0x68, 0x69,
2860            ]),
2861            Latin1Bidi::Bidi
2862        );
2863        assert_ne!(
2864            check_utf16_for_latin1_and_bidi(&[
2865                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFF, 0x62, 0x63, 0x64, 0x65,
2866                0x66, 0x67, 0x68, 0x69,
2867            ]),
2868            Latin1Bidi::Bidi
2869        );
2870        assert_eq!(
2871            check_utf16_for_latin1_and_bidi(&[
2872                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x0590, 0x62, 0x63, 0x64, 0x65,
2873                0x66, 0x67, 0x68, 0x69,
2874            ]),
2875            Latin1Bidi::Bidi
2876        );
2877        assert_eq!(
2878            check_utf16_for_latin1_and_bidi(&[
2879                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x08FF, 0x62, 0x63, 0x64, 0x65,
2880                0x66, 0x67, 0x68, 0x69,
2881            ]),
2882            Latin1Bidi::Bidi
2883        );
2884        assert_eq!(
2885            check_utf16_for_latin1_and_bidi(&[
2886                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x061C, 0x62, 0x63, 0x64, 0x65,
2887                0x66, 0x67, 0x68, 0x69,
2888            ]),
2889            Latin1Bidi::Bidi
2890        );
2891        assert_eq!(
2892            check_utf16_for_latin1_and_bidi(&[
2893                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFB1D, 0x62, 0x63, 0x64, 0x65,
2894                0x66, 0x67, 0x68, 0x69,
2895            ]),
2896            Latin1Bidi::Bidi
2897        );
2898        assert_eq!(
2899            check_utf16_for_latin1_and_bidi(&[
2900                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFB50, 0x62, 0x63, 0x64, 0x65,
2901                0x66, 0x67, 0x68, 0x69,
2902            ]),
2903            Latin1Bidi::Bidi
2904        );
2905        assert_eq!(
2906            check_utf16_for_latin1_and_bidi(&[
2907                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFDFF, 0x62, 0x63, 0x64, 0x65,
2908                0x66, 0x67, 0x68, 0x69,
2909            ]),
2910            Latin1Bidi::Bidi
2911        );
2912        assert_eq!(
2913            check_utf16_for_latin1_and_bidi(&[
2914                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFE70, 0x62, 0x63, 0x64, 0x65,
2915                0x66, 0x67, 0x68, 0x69,
2916            ]),
2917            Latin1Bidi::Bidi
2918        );
2919        assert_eq!(
2920            check_utf16_for_latin1_and_bidi(&[
2921                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xFEFE, 0x62, 0x63, 0x64, 0x65,
2922                0x66, 0x67, 0x68, 0x69,
2923            ]),
2924            Latin1Bidi::Bidi
2925        );
2926        assert_eq!(
2927            check_utf16_for_latin1_and_bidi(&[
2928                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x200F, 0x62, 0x63, 0x64, 0x65,
2929                0x66, 0x67, 0x68, 0x69,
2930            ]),
2931            Latin1Bidi::Bidi
2932        );
2933        assert_eq!(
2934            check_utf16_for_latin1_and_bidi(&[
2935                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202B, 0x62, 0x63, 0x64, 0x65,
2936                0x66, 0x67, 0x68, 0x69,
2937            ]),
2938            Latin1Bidi::Bidi
2939        );
2940        assert_eq!(
2941            check_utf16_for_latin1_and_bidi(&[
2942                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x202E, 0x62, 0x63, 0x64, 0x65,
2943                0x66, 0x67, 0x68, 0x69,
2944            ]),
2945            Latin1Bidi::Bidi
2946        );
2947        assert_eq!(
2948            check_utf16_for_latin1_and_bidi(&[
2949                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x2067, 0x62, 0x63, 0x64, 0x65,
2950                0x66, 0x67, 0x68, 0x69,
2951            ]),
2952            Latin1Bidi::Bidi
2953        );
2954        assert_eq!(
2955            check_utf16_for_latin1_and_bidi(&[
2956                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD802, 0x62, 0x63, 0x64, 0x65,
2957                0x66, 0x67, 0x68, 0x69,
2958            ]),
2959            Latin1Bidi::Bidi
2960        );
2961        assert_eq!(
2962            check_utf16_for_latin1_and_bidi(&[
2963                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD803, 0x62, 0x63, 0x64, 0x65,
2964                0x66, 0x67, 0x68, 0x69,
2965            ]),
2966            Latin1Bidi::Bidi
2967        );
2968        assert_eq!(
2969            check_utf16_for_latin1_and_bidi(&[
2970                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD83A, 0x62, 0x63, 0x64, 0x65,
2971                0x66, 0x67, 0x68, 0x69,
2972            ]),
2973            Latin1Bidi::Bidi
2974        );
2975        assert_eq!(
2976            check_utf16_for_latin1_and_bidi(&[
2977                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xD83B, 0x62, 0x63, 0x64, 0x65,
2978                0x66, 0x67, 0x68, 0x69,
2979            ]),
2980            Latin1Bidi::Bidi
2981        );
2982
2983        assert_eq!(
2984            check_utf16_for_latin1_and_bidi(&[
2985                0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x0590, 0x3041, 0x62, 0x63, 0x64,
2986                0x65, 0x66, 0x67, 0x68, 0x69,
2987            ]),
2988            Latin1Bidi::Bidi
2989        );
2990    }
2991
2992    #[inline(always)]
2993    pub fn reference_is_char_bidi(c: char) -> bool {
2994        match c {
2995            '\u{0590}'..='\u{08FF}'
2996            | '\u{FB1D}'..='\u{FDFF}'
2997            | '\u{FE70}'..='\u{FEFE}'
2998            | '\u{10800}'..='\u{10FFF}'
2999            | '\u{1E800}'..='\u{1EFFF}'
3000            | '\u{200F}'
3001            | '\u{202B}'
3002            | '\u{202E}'
3003            | '\u{2067}' => true,
3004            _ => false,
3005        }
3006    }
3007
3008    #[inline(always)]
3009    pub fn reference_is_utf16_code_unit_bidi(u: u16) -> bool {
3010        match u {
3011            0x0590..=0x08FF
3012            | 0xFB1D..=0xFDFF
3013            | 0xFE70..=0xFEFE
3014            | 0xD802
3015            | 0xD803
3016            | 0xD83A
3017            | 0xD83B
3018            | 0x200F
3019            | 0x202B
3020            | 0x202E
3021            | 0x2067 => true,
3022            _ => false,
3023        }
3024    }
3025
3026    #[test]
3027    #[cfg_attr(miri, ignore)] // Miri is too slow
3028    fn test_is_char_bidi_thoroughly() {
3029        for i in 0..0xD800u32 {
3030            let c: char = ::core::char::from_u32(i).unwrap();
3031            assert_eq!(is_char_bidi(c), reference_is_char_bidi(c));
3032        }
3033        for i in 0xE000..0x110000u32 {
3034            let c: char = ::core::char::from_u32(i).unwrap();
3035            assert_eq!(is_char_bidi(c), reference_is_char_bidi(c));
3036        }
3037    }
3038
3039    #[test]
3040    #[cfg_attr(miri, ignore)] // Miri is too slow
3041    fn test_is_utf16_code_unit_bidi_thoroughly() {
3042        for i in 0..0x10000u32 {
3043            let u = i as u16;
3044            assert_eq!(
3045                is_utf16_code_unit_bidi(u),
3046                reference_is_utf16_code_unit_bidi(u)
3047            );
3048        }
3049    }
3050
3051    #[test]
3052    #[cfg_attr(miri, ignore)] // Miri is too slow
3053    fn test_is_str_bidi_thoroughly() {
3054        let mut buf = [0; 4];
3055        for i in 0..0xD800u32 {
3056            let c: char = ::core::char::from_u32(i).unwrap();
3057            assert_eq!(
3058                is_str_bidi(c.encode_utf8(&mut buf[..])),
3059                reference_is_char_bidi(c)
3060            );
3061        }
3062        for i in 0xE000..0x110000u32 {
3063            let c: char = ::core::char::from_u32(i).unwrap();
3064            assert_eq!(
3065                is_str_bidi(c.encode_utf8(&mut buf[..])),
3066                reference_is_char_bidi(c)
3067            );
3068        }
3069    }
3070
3071    #[test]
3072    #[cfg_attr(miri, ignore)] // Miri is too slow
3073    fn test_is_utf8_bidi_thoroughly() {
3074        let mut buf = [0; 8];
3075        for i in 0..0xD800u32 {
3076            let c: char = ::core::char::from_u32(i).unwrap();
3077            let expect = reference_is_char_bidi(c);
3078            {
3079                let len = {
3080                    let bytes = c.encode_utf8(&mut buf[..]).as_bytes();
3081                    assert_eq!(is_utf8_bidi(bytes), expect);
3082                    bytes.len()
3083                };
3084                {
3085                    let tail = &mut buf[len..];
3086                    for b in tail.iter_mut() {
3087                        *b = 0;
3088                    }
3089                }
3090            }
3091            assert_eq!(is_utf8_bidi(&buf[..]), expect);
3092        }
3093        for i in 0xE000..0x110000u32 {
3094            let c: char = ::core::char::from_u32(i).unwrap();
3095            let expect = reference_is_char_bidi(c);
3096            {
3097                let len = {
3098                    let bytes = c.encode_utf8(&mut buf[..]).as_bytes();
3099                    assert_eq!(is_utf8_bidi(bytes), expect);
3100                    bytes.len()
3101                };
3102                {
3103                    let tail = &mut buf[len..];
3104                    for b in tail.iter_mut() {
3105                        *b = 0;
3106                    }
3107                }
3108            }
3109            assert_eq!(is_utf8_bidi(&buf[..]), expect);
3110        }
3111    }
3112
3113    #[test]
3114    #[cfg_attr(miri, ignore)] // Miri is too slow
3115    fn test_is_utf16_bidi_thoroughly() {
3116        let mut buf = [0; 32];
3117        for i in 0..0x10000u32 {
3118            let u = i as u16;
3119            buf[15] = u;
3120            assert_eq!(
3121                is_utf16_bidi(&buf[..]),
3122                reference_is_utf16_code_unit_bidi(u)
3123            );
3124        }
3125    }
3126
3127    #[test]
3128    fn test_is_utf8_bidi_edge_cases() {
3129        assert!(!is_utf8_bidi(b"\xD5\xBF\x61"));
3130        assert!(!is_utf8_bidi(b"\xD6\x80\x61"));
3131        assert!(!is_utf8_bidi(b"abc"));
3132        assert!(is_utf8_bidi(b"\xD5\xBF\xC2"));
3133        assert!(is_utf8_bidi(b"\xD6\x80\xC2"));
3134        assert!(is_utf8_bidi(b"ab\xC2"));
3135    }
3136
3137    #[test]
3138    fn test_decode_latin1() {
3139        match decode_latin1(b"ab") {
3140            Cow::Borrowed(s) => {
3141                assert_eq!(s, "ab");
3142            }
3143            Cow::Owned(_) => {
3144                unreachable!("Should have borrowed");
3145            }
3146        }
3147        assert_eq!(decode_latin1(b"a\xE4"), "a\u{E4}");
3148    }
3149
3150    #[test]
3151    fn test_encode_latin1_lossy() {
3152        match encode_latin1_lossy("ab") {
3153            Cow::Borrowed(s) => {
3154                assert_eq!(s, b"ab");
3155            }
3156            Cow::Owned(_) => {
3157                unreachable!("Should have borrowed");
3158            }
3159        }
3160        assert_eq!(encode_latin1_lossy("a\u{E4}"), &(b"a\xE4")[..]);
3161    }
3162
3163    #[test]
3164    fn test_convert_utf8_to_utf16_without_replacement() {
3165        let mut buf = [0u16; 5];
3166        assert_eq!(
3167            convert_utf8_to_utf16_without_replacement(b"ab", &mut buf[..2]),
3168            Some(2)
3169        );
3170        assert_eq!(buf[0], u16::from(b'a'));
3171        assert_eq!(buf[1], u16::from(b'b'));
3172        assert_eq!(buf[2], 0);
3173        assert_eq!(
3174            convert_utf8_to_utf16_without_replacement(b"\xC3\xA4c", &mut buf[..3]),
3175            Some(2)
3176        );
3177        assert_eq!(buf[0], 0xE4);
3178        assert_eq!(buf[1], u16::from(b'c'));
3179        assert_eq!(buf[2], 0);
3180        assert_eq!(
3181            convert_utf8_to_utf16_without_replacement(b"\xE2\x98\x83", &mut buf[..3]),
3182            Some(1)
3183        );
3184        assert_eq!(buf[0], 0x2603);
3185        assert_eq!(buf[1], u16::from(b'c'));
3186        assert_eq!(buf[2], 0);
3187        assert_eq!(
3188            convert_utf8_to_utf16_without_replacement(b"\xE2\x98\x83d", &mut buf[..4]),
3189            Some(2)
3190        );
3191        assert_eq!(buf[0], 0x2603);
3192        assert_eq!(buf[1], u16::from(b'd'));
3193        assert_eq!(buf[2], 0);
3194        assert_eq!(
3195            convert_utf8_to_utf16_without_replacement(b"\xE2\x98\x83\xC3\xA4", &mut buf[..5]),
3196            Some(2)
3197        );
3198        assert_eq!(buf[0], 0x2603);
3199        assert_eq!(buf[1], 0xE4);
3200        assert_eq!(buf[2], 0);
3201        assert_eq!(
3202            convert_utf8_to_utf16_without_replacement(b"\xF0\x9F\x93\x8E", &mut buf[..4]),
3203            Some(2)
3204        );
3205        assert_eq!(buf[0], 0xD83D);
3206        assert_eq!(buf[1], 0xDCCE);
3207        assert_eq!(buf[2], 0);
3208        assert_eq!(
3209            convert_utf8_to_utf16_without_replacement(b"\xF0\x9F\x93\x8Ee", &mut buf[..5]),
3210            Some(3)
3211        );
3212        assert_eq!(buf[0], 0xD83D);
3213        assert_eq!(buf[1], 0xDCCE);
3214        assert_eq!(buf[2], u16::from(b'e'));
3215        assert_eq!(
3216            convert_utf8_to_utf16_without_replacement(b"\xF0\x9F\x93", &mut buf[..5]),
3217            None
3218        );
3219    }
3220}