@@ -5,6 +5,9 @@ use core::ascii::EscapeDefault;
55use crate :: fmt:: { self , Write } ;
66use crate :: { ascii, iter, mem, ops} ;
77
8+ #[ cfg( kani) ]
9+ use crate :: kani;
10+
811#[ cfg( not( test) ) ]
912impl [ u8 ] {
1013 /// Checks if all bytes in this slice are within the ASCII range.
@@ -398,6 +401,10 @@ const fn is_ascii(s: &[u8]) -> bool {
398401 // Read subsequent words until the last aligned word, excluding the last
399402 // aligned word by itself to be done in tail check later, to ensure that
400403 // tail is always one `usize` at most to extra branch `byte_pos == len`.
404+ #[ safety:: loop_invariant( byte_pos <= len
405+ && byte_pos >= offset_to_aligned
406+ && word_ptr. addr( ) >= start. addr( ) + offset_to_aligned
407+ && byte_pos == word_ptr. addr( ) - start. addr( ) ) ]
401408 while byte_pos < len - USIZE_SIZE {
402409 // Sanity check that the read is in bounds
403410 debug_assert ! ( byte_pos + USIZE_SIZE <= len) ;
@@ -432,3 +439,28 @@ const fn is_ascii(s: &[u8]) -> bool {
432439
433440 !contains_nonascii ( last_word)
434441}
442+
443+ #[ cfg( kani) ]
444+ #[ unstable( feature = "kani" , issue = "none" ) ]
445+ pub mod verify {
446+ use super :: * ;
447+
448+ #[ kani:: proof]
449+ #[ kani:: unwind( 8 ) ]
450+ pub fn check_is_ascii ( ) {
451+ if kani:: any ( ) {
452+ // TODO: ARR_SIZE can be much larger with cbmc argument
453+ // `--arrays-uf-always`
454+ const ARR_SIZE : usize = 1000 ;
455+ let mut x: [ u8 ; ARR_SIZE ] = kani:: any ( ) ;
456+ let mut xs = kani:: slice:: any_slice_of_array_mut ( & mut x) ;
457+ is_ascii ( xs) ;
458+ } else {
459+ let ptr = kani:: any_where :: < usize , _ > ( |val| * val != 0 ) as * const u8 ;
460+ kani:: assume ( ptr. is_aligned ( ) ) ;
461+ unsafe {
462+ assert_eq ! ( is_ascii( crate :: slice:: from_raw_parts( ptr, 0 ) ) , true ) ;
463+ }
464+ }
465+ }
466+ }
0 commit comments