Skip to content

Commit 8cd8535

Browse files
UbuntuCopilot
andcommitted
Add slice ASCII and iterator specifications
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 37c58501-5f9f-4a00-b559-69d47c22f399
1 parent 11eda20 commit 8cd8535

1 file changed

Lines changed: 313 additions & 1 deletion

File tree

source/vstd/std_specs/slice.rs

Lines changed: 313 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ use super::range::{slice_range_end, slice_range_start, slice_range_valid};
77
use core::ops::{
88
Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
99
};
10-
use core::slice::{Iter, SliceIndex};
10+
use core::slice::{
11+
ChunksExact, ChunksExactMut, EscapeAscii, Iter, IterMut, RChunksExact, RChunksExactMut,
12+
SliceIndex, Windows,
13+
};
14+
use core::str::Utf8Chunks;
1115

1216
use verus as verus_;
1317

@@ -408,6 +412,314 @@ pub assume_specification<T: Copy, R: core::ops::RangeBounds<usize>>[ <[T]>::copy
408412
),
409413
;
410414

415+
#[verifier::external_type_specification]
416+
#[verifier::external_body]
417+
#[verifier::reject_recursive_types(T)]
418+
pub struct ExIterMut<'a, T: 'a>(IterMut<'a, T>);
419+
420+
#[verifier::external_type_specification]
421+
#[verifier::external_body]
422+
#[verifier::reject_recursive_types(T)]
423+
pub struct ExChunksExact<'a, T: 'a>(ChunksExact<'a, T>);
424+
425+
#[verifier::external_type_specification]
426+
#[verifier::external_body]
427+
#[verifier::reject_recursive_types(T)]
428+
pub struct ExChunksExactMut<'a, T: 'a>(ChunksExactMut<'a, T>);
429+
430+
#[verifier::external_type_specification]
431+
#[verifier::external_body]
432+
#[verifier::reject_recursive_types(T)]
433+
pub struct ExRChunksExact<'a, T: 'a>(RChunksExact<'a, T>);
434+
435+
#[verifier::external_type_specification]
436+
#[verifier::external_body]
437+
#[verifier::reject_recursive_types(T)]
438+
pub struct ExRChunksExactMut<'a, T: 'a>(RChunksExactMut<'a, T>);
439+
440+
#[verifier::external_type_specification]
441+
#[verifier::external_body]
442+
#[verifier::reject_recursive_types(T)]
443+
pub struct ExWindows<'a, T: 'a>(Windows<'a, T>);
444+
445+
#[verifier::external_type_specification]
446+
#[verifier::external_body]
447+
pub struct ExUtf8Chunks<'a>(Utf8Chunks<'a>);
448+
449+
#[verifier::external_type_specification]
450+
#[verifier::external_body]
451+
pub struct ExEscapeAscii<'a>(EscapeAscii<'a>);
452+
453+
pub ghost struct SliceIteratorView<T> {
454+
pub source: Seq<T>,
455+
pub remaining: Seq<T>,
456+
pub yielded_prefix: Seq<T>,
457+
pub remainder: Seq<T>,
458+
pub chunk_size: int,
459+
pub reverse: bool,
460+
}
461+
462+
pub uninterp spec fn slice_iterator_view<I, T>(iter: I) -> SliceIteratorView<T>;
463+
464+
pub open spec fn slice_iterator_well_formed<T>(view: SliceIteratorView<T>) -> bool {
465+
0 <= view.chunk_size && view.remainder.len() <= view.source.len()
466+
}
467+
468+
pub broadcast axiom fn axiom_slice_iterator_view_well_formed<I, T>(iter: I)
469+
ensures
470+
slice_iterator_well_formed(#[trigger] slice_iterator_view::<I, T>(iter)),
471+
;
472+
473+
pub open spec fn utf8_chunk_partition<I>(iter: I, source: Seq<u8>) -> bool {
474+
let view = slice_iterator_view::<I, u8>(iter);
475+
slice_iterator_well_formed(view)
476+
&& view.source == source
477+
&& view.remaining == source
478+
&& view.yielded_prefix == Seq::empty()
479+
&& view.remainder == Seq::empty()
480+
&& view.chunk_size == 0
481+
&& !view.reverse
482+
}
483+
484+
pub open spec fn ascii_is_uppercase(byte: u8) -> bool {
485+
0x41 <= (byte as int) && (byte as int) <= 0x5a
486+
}
487+
488+
pub open spec fn ascii_is_lowercase(byte: u8) -> bool {
489+
0x61 <= (byte as int) && (byte as int) <= 0x7a
490+
}
491+
492+
pub open spec fn ascii_lower_byte(byte: u8) -> u8 {
493+
if ascii_is_uppercase(byte) {
494+
((byte as int) + 0x20) as u8
495+
} else {
496+
byte
497+
}
498+
}
499+
500+
pub open spec fn ascii_upper_byte(byte: u8) -> u8 {
501+
if ascii_is_lowercase(byte) {
502+
((byte as int) - 0x20) as u8
503+
} else {
504+
byte
505+
}
506+
}
507+
508+
pub open spec fn ascii_is_whitespace(byte: u8) -> bool {
509+
byte == 0x09u8
510+
|| byte == 0x0au8
511+
|| byte == 0x0cu8
512+
|| byte == 0x0du8
513+
|| byte == 0x20u8
514+
}
515+
516+
pub open spec fn ascii_lower_seq(seq: Seq<u8>) -> Seq<u8> {
517+
Seq::new(seq.len(), |i: int| ascii_lower_byte(seq[i]))
518+
}
519+
520+
pub open spec fn ascii_upper_seq(seq: Seq<u8>) -> Seq<u8> {
521+
Seq::new(seq.len(), |i: int| ascii_upper_byte(seq[i]))
522+
}
523+
524+
pub open spec fn ascii_eq_ignore_case(left: Seq<u8>, right: Seq<u8>) -> bool {
525+
left.len() == right.len()
526+
&& forall|i: int| 0 <= i < left.len()
527+
==> ascii_lower_byte(left[i]) == ascii_lower_byte(right[i])
528+
}
529+
530+
pub open spec fn ascii_trim_start_boundary(seq: Seq<u8>, i: int) -> bool {
531+
0 <= i <= seq.len()
532+
&& (forall|j: int| 0 <= j < i ==> #[trigger] ascii_is_whitespace(seq[j]))
533+
&& (i < seq.len() ==> !ascii_is_whitespace(seq[i]))
534+
}
535+
536+
pub open spec fn ascii_trim_end_boundary(seq: Seq<u8>, i: int) -> bool {
537+
0 <= i <= seq.len()
538+
&& (forall|j: int| i <= j < seq.len() ==> #[trigger] ascii_is_whitespace(seq[j]))
539+
&& (0 < i ==> !ascii_is_whitespace(seq[i - 1]))
540+
}
541+
542+
pub open spec fn ascii_trim_start_index(seq: Seq<u8>) -> int {
543+
choose|i: int| #[trigger] ascii_trim_start_boundary(seq, i)
544+
}
545+
546+
pub open spec fn ascii_trim_end_index(seq: Seq<u8>) -> int {
547+
choose|i: int| #[trigger] ascii_trim_end_boundary(seq, i)
548+
}
549+
550+
pub open spec fn ascii_trim_start_result(seq: Seq<u8>, ret: &[u8]) -> bool {
551+
0 <= ascii_trim_start_index(seq) <= seq.len()
552+
&& ret@ == seq.subrange(ascii_trim_start_index(seq), seq.len() as int)
553+
&& (forall|i: int| 0 <= i < ascii_trim_start_index(seq)
554+
==> ascii_is_whitespace(seq[i]))
555+
&& (ascii_trim_start_index(seq) < seq.len()
556+
==> !ascii_is_whitespace(seq[ascii_trim_start_index(seq)]))
557+
}
558+
559+
pub open spec fn ascii_trim_end_result(seq: Seq<u8>, ret: &[u8]) -> bool {
560+
0 <= ascii_trim_end_index(seq) <= seq.len()
561+
&& ret@ == seq.subrange(0, ascii_trim_end_index(seq))
562+
&& (forall|i: int| ascii_trim_end_index(seq) <= i < seq.len()
563+
==> ascii_is_whitespace(seq[i]))
564+
&& (0 < ascii_trim_end_index(seq)
565+
==> !ascii_is_whitespace(seq[ascii_trim_end_index(seq) - 1]))
566+
}
567+
568+
pub open spec fn ascii_trim_source_body_result(seq: Seq<u8>, ret: &[u8]) -> bool {
569+
let start = ascii_trim_start_index(seq);
570+
let after_start = seq.subrange(start, seq.len() as int);
571+
let end = ascii_trim_end_index(after_start);
572+
0 <= start <= seq.len()
573+
&& 0 <= end <= after_start.len()
574+
&& ret@ == seq.subrange(start, start + end)
575+
&& (forall|i: int| 0 <= i < start ==> ascii_is_whitespace(seq[i]))
576+
&& (forall|i: int| start + end <= i < seq.len() ==> ascii_is_whitespace(seq[i]))
577+
}
578+
579+
pub open spec fn ascii_lower_hex_digit(nibble: int) -> u8
580+
recommends
581+
0 <= nibble < 16,
582+
{
583+
if nibble < 10 {
584+
(0x30 + nibble) as u8
585+
} else {
586+
(0x61 + (nibble - 10)) as u8
587+
}
588+
}
589+
590+
pub open spec fn ascii_escape_byte(byte: u8) -> Seq<u8> {
591+
if byte == 0x09u8 {
592+
seq![0x5cu8, 0x74u8]
593+
} else if byte == 0x0du8 {
594+
seq![0x5cu8, 0x72u8]
595+
} else if byte == 0x0au8 {
596+
seq![0x5cu8, 0x6eu8]
597+
} else if byte == 0x27u8 {
598+
seq![0x5cu8, 0x27u8]
599+
} else if byte == 0x22u8 {
600+
seq![0x5cu8, 0x22u8]
601+
} else if byte == 0x5cu8 {
602+
seq![0x5cu8, 0x5cu8]
603+
} else if 0x20 <= (byte as int) && (byte as int) <= 0x7e {
604+
seq![byte]
605+
} else {
606+
seq![
607+
0x5cu8,
608+
0x78u8,
609+
ascii_lower_hex_digit((byte as int) / 16),
610+
ascii_lower_hex_digit((byte as int) % 16),
611+
]
612+
}
613+
}
614+
615+
pub open spec fn ascii_escape_seq(seq: Seq<u8>) -> Seq<u8> {
616+
seq.flat_map(|byte: u8| ascii_escape_byte(byte))
617+
}
618+
619+
pub assume_specification<'a, T>[ <[T]>::iter_mut ](
620+
slice: &'a mut [T],
621+
) -> (iter: IterMut<'a, T>)
622+
ensures
623+
slice_iterator_view::<IterMut<'a, T>, T>(iter).source == old(slice)@,
624+
slice_iterator_view::<IterMut<'a, T>, T>(iter).remaining == old(slice)@,
625+
final(slice)@ == old(slice)@,
626+
;
627+
628+
pub assume_specification<'a, T>[ <[T]>::windows ](
629+
slice: &'a [T],
630+
size: usize,
631+
) -> (iter: Windows<'a, T>)
632+
requires
633+
size != 0,
634+
ensures
635+
slice_iterator_view::<Windows<'a, T>, T>(iter).source == slice@,
636+
slice_iterator_view::<Windows<'a, T>, T>(iter).remaining == slice@,
637+
slice_iterator_view::<Windows<'a, T>, T>(iter).yielded_prefix == Seq::empty(),
638+
slice_iterator_view::<Windows<'a, T>, T>(iter).remainder == Seq::empty(),
639+
slice_iterator_view::<Windows<'a, T>, T>(iter).chunk_size == size as int,
640+
!slice_iterator_view::<Windows<'a, T>, T>(iter).reverse,
641+
;
642+
643+
pub assume_specification<'a, T>[ ChunksExact::<'a, T>::remainder ](
644+
iter: &ChunksExact<'a, T>,
645+
) -> (ret: &'a [T])
646+
ensures
647+
ret@ == slice_iterator_view::<&ChunksExact<'a, T>, T>(iter).remainder,
648+
ret@.len() < slice_iterator_view::<&ChunksExact<'a, T>, T>(iter).chunk_size,
649+
;
650+
651+
pub assume_specification<'a, T>[ ChunksExactMut::<'a, T>::into_remainder ](
652+
iter: ChunksExactMut<'a, T>,
653+
) -> (ret: &'a mut [T])
654+
ensures
655+
ret@ == slice_iterator_view::<ChunksExactMut<'a, T>, T>(iter).remainder,
656+
ret@.len() < slice_iterator_view::<ChunksExactMut<'a, T>, T>(iter).chunk_size,
657+
;
658+
659+
pub assume_specification<'a, T>[ RChunksExact::<'a, T>::remainder ](
660+
iter: &RChunksExact<'a, T>,
661+
) -> (ret: &'a [T])
662+
ensures
663+
ret@ == slice_iterator_view::<&RChunksExact<'a, T>, T>(iter).remainder,
664+
ret@.len() < slice_iterator_view::<&RChunksExact<'a, T>, T>(iter).chunk_size,
665+
;
666+
667+
pub assume_specification<'a, T>[ RChunksExactMut::<'a, T>::into_remainder ](
668+
iter: RChunksExactMut<'a, T>,
669+
) -> (ret: &'a mut [T])
670+
ensures
671+
ret@ == slice_iterator_view::<RChunksExactMut<'a, T>, T>(iter).remainder,
672+
ret@.len() < slice_iterator_view::<RChunksExactMut<'a, T>, T>(iter).chunk_size,
673+
;
674+
675+
pub assume_specification<'a>[ <[u8]>::utf8_chunks ](
676+
slice: &'a [u8],
677+
) -> (iter: Utf8Chunks<'a>)
678+
ensures
679+
utf8_chunk_partition::<Utf8Chunks<'a>>(iter, slice@),
680+
;
681+
682+
pub assume_specification[ <[u8]>::eq_ignore_ascii_case ](
683+
slice: &[u8],
684+
other: &[u8],
685+
) -> (ret: bool)
686+
ensures
687+
ret <==> ascii_eq_ignore_case(slice@, other@),
688+
;
689+
690+
pub assume_specification<'a>[ <[u8]>::escape_ascii ](
691+
slice: &'a [u8],
692+
) -> (iter: EscapeAscii<'a>)
693+
ensures
694+
slice_iterator_view::<EscapeAscii<'a>, u8>(iter).source == slice@,
695+
slice_iterator_view::<EscapeAscii<'a>, u8>(iter).remaining == ascii_escape_seq(slice@),
696+
;
697+
698+
pub assume_specification[ <[u8]>::make_ascii_lowercase ](slice: &mut [u8])
699+
ensures
700+
final(slice)@ == ascii_lower_seq(old(slice)@),
701+
;
702+
703+
pub assume_specification[ <[u8]>::make_ascii_uppercase ](slice: &mut [u8])
704+
ensures
705+
final(slice)@ == ascii_upper_seq(old(slice)@),
706+
;
707+
708+
pub assume_specification[ <[u8]>::trim_ascii ](slice: &[u8]) -> (ret: &[u8])
709+
ensures
710+
ascii_trim_source_body_result(slice@, ret),
711+
;
712+
713+
pub assume_specification[ <[u8]>::trim_ascii_end ](slice: &[u8]) -> (ret: &[u8])
714+
ensures
715+
ascii_trim_end_result(slice@, ret),
716+
;
717+
718+
pub assume_specification[ <[u8]>::trim_ascii_start ](slice: &[u8]) -> (ret: &[u8])
719+
ensures
720+
ascii_trim_start_result(slice@, ret),
721+
;
722+
411723
pub broadcast group group_slice_axioms {
412724
axiom_slice_get_range,
413725
axiom_slice_get_range_to,

0 commit comments

Comments
 (0)