@@ -2795,7 +2795,7 @@ impl [u8] {
2795
2795
#[ stable( feature = "ascii_methods_on_intrinsics" , since = "1.23.0" ) ]
2796
2796
#[ inline]
2797
2797
pub fn is_ascii ( & self ) -> bool {
2798
- self . iter ( ) . all ( |b| b . is_ascii ( ) )
2798
+ is_ascii ( self )
2799
2799
}
2800
2800
2801
2801
/// Checks that two slices are an ASCII case-insensitive match.
@@ -2843,6 +2843,106 @@ impl [u8] {
2843
2843
}
2844
2844
}
2845
2845
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
+
2846
2946
#[ stable( feature = "rust1" , since = "1.0.0" ) ]
2847
2947
impl < T , I > ops:: Index < I > for [ T ]
2848
2948
where
0 commit comments