@@ -2795,7 +2795,7 @@ impl [u8] {
27952795 #[ stable( feature = "ascii_methods_on_intrinsics" , since = "1.23.0" ) ]
27962796 #[ inline]
27972797 pub fn is_ascii ( & self ) -> bool {
2798- self . iter ( ) . all ( |b| b . is_ascii ( ) )
2798+ is_ascii ( self )
27992799 }
28002800
28012801 /// Checks that two slices are an ASCII case-insensitive match.
@@ -2843,6 +2843,106 @@ impl [u8] {
28432843 }
28442844}
28452845
2846+ /// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed
2847+ /// from `../str/mod.rs`, which does something similar for utf8 validation.
2848+ #[ inline]
2849+ fn contains_nonascii ( v : usize ) -> bool {
2850+ const NONASCII_MASK : usize = 0x80808080_80808080u64 as usize ;
2851+ ( NONASCII_MASK & v) != 0
2852+ }
2853+
2854+ /// Optimized ASCII test that will use usize-at-a-time operations instead of
2855+ /// byte-at-a-time operations (when possible).
2856+ ///
2857+ /// The algorithm we use here is pretty simple. If `s` is too short, we just
2858+ /// check each byte and be done with it. Otherwise:
2859+ ///
2860+ /// - Read the first word with an unaligned load.
2861+ /// - Align the pointer, read subsequent words until end with aligned loads.
2862+ /// - If there's a tail, the last `usize` from `s` with an unaligned load.
2863+ ///
2864+ /// If any of these loads produces something for which `contains_nonascii`
2865+ /// (above) returns true, then we know the answer is false.
2866+ #[ inline]
2867+ fn is_ascii ( s : & [ u8 ] ) -> bool {
2868+ const USIZE_SIZE : usize = mem:: size_of :: < usize > ( ) ;
2869+
2870+ let len = s. len ( ) ;
2871+ let align_offset = s. as_ptr ( ) . align_offset ( USIZE_SIZE ) ;
2872+
2873+ // If we wouldn't gain anything from the word-at-a-time implementation, fall
2874+ // back to a scalar loop.
2875+ //
2876+ // We also do this for architectures where `size_of::<usize>()` isn't
2877+ // sufficient alignment for `usize`, because it's a weird edge case.
2878+ if len < USIZE_SIZE || len < align_offset || USIZE_SIZE < mem:: align_of :: < usize > ( ) {
2879+ return s. iter ( ) . all ( |b| b. is_ascii ( ) ) ;
2880+ }
2881+
2882+ // We always read the first word unaligned, which means `align_offset` is
2883+ // 0, we'd read the same value again for the aligned read.
2884+ let offset_to_aligned = if align_offset == 0 { USIZE_SIZE } else { align_offset } ;
2885+
2886+ let start = s. as_ptr ( ) ;
2887+ // SAFETY: We verify `len < USIZE_SIZE` above.
2888+ let first_word = unsafe { ( start as * const usize ) . read_unaligned ( ) } ;
2889+
2890+ if contains_nonascii ( first_word) {
2891+ return false ;
2892+ }
2893+ // We checked this above, somewhat implicitly. Note that `offset_to_aligned`
2894+ // is either `align_offset` or `USIZE_SIZE`, both of are explicitly checked
2895+ // above.
2896+ debug_assert ! ( offset_to_aligned <= len) ;
2897+
2898+ // word_ptr is the (properly aligned) usize ptr we use to read the middle chunk of the slice.
2899+ let mut word_ptr = unsafe { start. add ( offset_to_aligned) as * const usize } ;
2900+
2901+ // `byte_pos` is the byte index of `word_ptr`, used for loop end checks.
2902+ let mut byte_pos = offset_to_aligned;
2903+
2904+ // Paranoia check about alignment, since we're about to do a bunch of
2905+ // unaligned loads. In practice this should be impossible barring a bug in
2906+ // `align_offset` though.
2907+ debug_assert_eq ! ( ( word_ptr as usize ) % mem:: align_of:: <usize >( ) , 0 ) ;
2908+
2909+ while byte_pos <= len - USIZE_SIZE {
2910+ debug_assert ! (
2911+ // Sanity check that the read is in bounds
2912+ ( word_ptr as usize + USIZE_SIZE ) <= ( start. wrapping_add( len) as usize ) &&
2913+ // And that our assumptions about `byte_pos` hold.
2914+ ( word_ptr as usize ) - ( start as usize ) == byte_pos
2915+ ) ;
2916+
2917+ // Safety: We know `word_ptr` is properly aligned (because of
2918+ // `align_offset`), and we know that we have enough bytes between `word_ptr` and the end
2919+ let word = unsafe { word_ptr. read ( ) } ;
2920+ if contains_nonascii ( word) {
2921+ return false ;
2922+ }
2923+
2924+ byte_pos += USIZE_SIZE ;
2925+ // SAFETY: We know that `byte_pos <= len - USIZE_SIZE`, which means that
2926+ // after this `add`, `word_ptr` will be at most one-past-the-end.
2927+ word_ptr = unsafe { word_ptr. add ( 1 ) } ;
2928+ }
2929+
2930+ // If we have anything left over, it should be at-most 1 usize worth of bytes,
2931+ // which we check with a read_unaligned.
2932+ if byte_pos == len {
2933+ return true ;
2934+ }
2935+
2936+ // Sanity check to ensure there really is only one `usize` left. This should
2937+ // be guaranteed by our loop condition.
2938+ debug_assert ! ( byte_pos < len && len - byte_pos < USIZE_SIZE ) ;
2939+
2940+ // SAFETY: This relies on `len >= USIZE_SIZE`, which we check at the start.
2941+ let last_word = unsafe { ( start. add ( len - USIZE_SIZE ) as * const usize ) . read_unaligned ( ) } ;
2942+
2943+ !contains_nonascii ( last_word)
2944+ }
2945+
28462946#[ stable( feature = "rust1" , since = "1.0.0" ) ]
28472947impl < T , I > ops:: Index < I > for [ T ]
28482948where
0 commit comments