vstd/
string.rs

1#![feature(rustc_attrs)]
2#![allow(unused_imports)]
3
4#[cfg(all(feature = "alloc", not(verus_verify_core)))]
5use alloc::str::Chars;
6#[cfg(all(feature = "alloc", not(verus_verify_core)))]
7use alloc::string::{self, String, ToString};
8
9#[cfg(all(feature = "alloc", not(verus_verify_core)))]
10use super::pervasive::{ForLoopGhostIterator, ForLoopGhostIteratorNew};
11use super::prelude::*;
12use super::seq::Seq;
13use super::slice::*;
14use super::utf8::*;
15use super::view::*;
16
17verus! {
18
19broadcast use {super::seq::group_seq_axioms, super::slice::group_slice_axioms};
20
21#[cfg(not(verus_verify_core))]
22impl View for str {
23    type V = Seq<char>;
24
25    uninterp spec fn view(&self) -> Seq<char>;
26}
27
28#[cfg(not(verus_verify_core))]
29impl DeepView for str {
30    type V = Seq<char>;
31
32    open spec fn deep_view(&self) -> Seq<char> {
33        self.view()
34    }
35}
36
37#[cfg(not(verus_verify_core))]
38pub trait StringSliceAdditionalSpecFns {
39    spec fn spec_bytes(&self) -> Seq<u8>;
40}
41
42#[cfg(not(verus_verify_core))]
43impl StringSliceAdditionalSpecFns for str {
44    open spec fn spec_bytes(&self) -> Seq<u8> {
45        encode_utf8(self@)
46    }
47}
48
49#[cfg(not(verus_verify_core))]
50pub open spec fn is_ascii(s: &str) -> bool {
51    is_ascii_chars(s@)
52}
53
54#[cfg(not(verus_verify_core))]
55pub broadcast proof fn is_ascii_spec_bytes(s: &str)
56    ensures
57        #[trigger] is_ascii(s) ==> #[trigger] s.spec_bytes() =~= Seq::new(
58            s@.len(),
59            |i| s@.index(i) as u8,
60        ),
61{
62    if (is_ascii(s)) {
63        is_ascii_chars_encode_utf8(s@);
64    }
65}
66
67#[cfg(not(verus_verify_core))]
68pub broadcast proof fn is_ascii_concat(s1: &str, s2: &str, s3: &str)
69    requires
70        s1@ =~= s2@ + s3@,
71    ensures
72        #![trigger s2@ + s3@, is_ascii(s1), is_ascii(s2), is_ascii(s3)]
73        is_ascii(s1) <==> is_ascii(s2) && is_ascii(s3),
74{
75    broadcast use is_ascii_chars_concat;
76
77    if (is_ascii(s1)) {
78        is_ascii_chars_concat(s1@, s2@, s3@);
79    }
80}
81
82#[cfg(not(verus_verify_core))]
83#[verifier::when_used_as_spec(is_ascii)]
84pub assume_specification[ str::is_ascii ](s: &str) -> (b: bool)
85    ensures
86        b == is_ascii(s),
87;
88
89#[cfg(all(feature = "alloc", not(verus_verify_core)))]
90use crate::alloc::borrow::ToOwned;
91
92#[cfg(all(feature = "alloc", not(verus_verify_core)))]
93pub assume_specification[ str::to_owned ](s: &str) -> (res: String)
94    ensures
95        s@ == res@,
96;
97
98#[cfg(not(verus_verify_core))]
99pub assume_specification[ str::as_bytes ](s: &str) -> (b: &[u8])
100    ensures
101        b@ == s.spec_bytes(),
102;
103
104#[cfg(not(verus_verify_core))]
105#[verifier::allow_in_spec]
106pub assume_specification[ str::len ](s: &str) -> usize
107    returns
108        s.spec_bytes().len() as usize,
109;
110
111#[cfg(not(verus_verify_core))]
112#[verifier::allow_in_spec]
113pub assume_specification[ str::is_empty ](s: &str) -> bool
114    returns
115        s@.len() == 0,
116;
117
118#[cfg(not(verus_verify_core))]
119#[verifier::allow_in_spec]
120pub assume_specification[ str::is_char_boundary ](s: &str, index: usize) -> bool
121    returns
122        is_char_boundary(s.spec_bytes(), index as int),
123;
124
125#[cfg(not(verus_verify_core))]
126pub assume_specification[ str::split_at ](s: &str, mid: usize) -> (res: (&str, &str))
127    requires
128        is_char_boundary(s.spec_bytes(), mid as int),
129    ensures
130        res.0.spec_bytes() =~= s.spec_bytes().subrange(0, mid as int),
131        res.1.spec_bytes() =~= s.spec_bytes().subrange(mid as int, s.spec_bytes().len() as int),
132;
133
134#[cfg(not(verus_verify_core))]
135pub assume_specification[ str::from_utf8_unchecked ](v: &[u8]) -> (res: &str)
136    requires
137        valid_utf8(v@),
138    ensures
139        res.spec_bytes() =~= v@,
140;
141
142#[cfg(all(feature = "alloc", not(verus_verify_core)))]
143pub uninterp spec fn to_string_from_display_ensures<T: core::fmt::Display + ?Sized>(
144    t: &T,
145    s: String,
146) -> bool;
147
148#[cfg(all(feature = "alloc", not(verus_verify_core)))]
149pub broadcast proof fn to_string_from_display_ensures_for_str(t: &str, res: String)
150    ensures
151        #[trigger] to_string_from_display_ensures::<str>(t, res) <==> (t@ == res@),
152{
153    admit();
154}
155
156#[cfg(all(feature = "alloc", not(verus_verify_core)))]
157pub assume_specification<T: core::fmt::Display + ?Sized>[ <T as ToString>::to_string ](
158    t: &T,
159) -> (res: String)
160    ensures
161        to_string_from_display_ensures::<T>(t, res),
162;
163
164#[cfg(not(verus_verify_core))]
165#[verifier::external]
166pub trait StrSliceExecFns {
167    fn unicode_len(&self) -> usize;
168
169    fn get_char(&self, i: usize) -> char;
170
171    fn substring_ascii<'a>(&'a self, from: usize, to: usize) -> &'a str;
172
173    fn substring_char<'a>(&'a self, from: usize, to: usize) -> &'a str;
174
175    fn get_ascii(&self, i: usize) -> u8;
176
177    #[cfg(feature = "alloc")]
178    fn as_bytes_vec(&self) -> alloc::vec::Vec<u8>;
179}
180
181#[cfg(not(verus_verify_core))]
182impl StrSliceExecFns for str {
183    /// The len() function in rust returns the byte length.
184    /// It is more useful to talk about the length of characters and therefore this function was added.
185    /// Please note that this function counts the unicode variation selectors as characters.
186    /// Warning: O(n)
187    #[verifier::external_body]
188    fn unicode_len(&self) -> (l: usize)
189        ensures
190            l as nat == self@.len(),
191    {
192        self.chars().count()
193    }
194
195    /// Warning: O(n) not O(1) due to unicode decoding needed
196    #[verifier::external_body]
197    fn get_char(&self, i: usize) -> (c: char)
198        requires
199            i < self@.len(),
200        ensures
201            self@.index(i as int) == c,
202    {
203        self.chars().nth(i).unwrap()
204    }
205
206    #[verifier::external_body]
207    fn substring_ascii<'a>(&'a self, from: usize, to: usize) -> (ret: &'a str)
208        requires
209            self.is_ascii(),
210            from <= to <= self@.len(),
211        ensures
212            ret@ == self@.subrange(from as int, to as int),
213            ret.is_ascii(),
214    {
215        // Range::index panics if from > to or from > self@.len()
216        &self[from..to]
217    }
218
219    #[verifier::external_body]
220    fn substring_char<'a>(&'a self, from: usize, to: usize) -> (ret: &'a str)
221        requires
222            from <= to <= self@.len(),
223        ensures
224            ret@ == self@.subrange(from as int, to as int),
225    {
226        let mut char_pos = 0;
227        let mut byte_start = None;
228        let mut byte_end = None;
229        let mut byte_pos = 0;
230        let mut it = self.chars();
231        loop {
232            if char_pos == from {
233                byte_start = Some(byte_pos);
234            }
235            if char_pos == to {
236                byte_end = Some(byte_pos);
237                break ;
238            }
239            if let Some(c) = it.next() {
240                char_pos += 1;
241                byte_pos += c.len_utf8();
242            } else {
243                break ;
244            }
245        }
246        let byte_start = byte_start.unwrap();
247        let byte_end = byte_end.unwrap();
248        // Range::index panics if from > to or from > self@.len()
249        &self[byte_start..byte_end]
250    }
251
252    fn get_ascii(&self, i: usize) -> (b: u8)
253        requires
254            self.is_ascii(),
255            i < self@.len(),
256        ensures
257            self@.index(i as int) as u8 == b,
258    {
259        broadcast use is_ascii_spec_bytes;
260        // panics if i is not a valid index
261
262        self.as_bytes()[i]
263    }
264
265    #[cfg(feature = "alloc")]
266    fn as_bytes_vec(&self) -> (ret: alloc::vec::Vec<u8>)
267        ensures
268            ret@ == self.spec_bytes(),
269    {
270        slice_to_vec(self.as_bytes())
271    }
272}
273
274#[cfg(not(verus_verify_core))]
275pub broadcast axiom fn axiom_str_literal_len<'a>(s: &'a str)
276    ensures
277        #[trigger] s@.len() == strslice_len(s),
278;
279
280#[cfg(not(verus_verify_core))]
281pub broadcast axiom fn axiom_str_literal_get_char<'a>(s: &'a str, i: int)
282    ensures
283        #[trigger] s@.index(i) == strslice_get_char(s, i),
284;
285
286#[cfg(all(not(feature = "alloc"), not(verus_verify_core)))]
287pub broadcast group group_string_axioms {
288    axiom_str_literal_len,
289    axiom_str_literal_get_char,
290    is_ascii_spec_bytes,
291    is_ascii_concat,
292}
293
294#[cfg(all(feature = "alloc", not(verus_verify_core)))]
295pub broadcast group group_string_axioms {
296    axiom_str_literal_len,
297    axiom_str_literal_get_char,
298    to_string_from_display_ensures_for_str,
299    is_ascii_spec_bytes,
300    is_ascii_concat,
301}
302
303#[cfg(all(feature = "alloc", not(verus_verify_core)))]
304impl View for String {
305    type V = Seq<char>;
306
307    uninterp spec fn view(&self) -> Seq<char>;
308}
309
310#[cfg(all(feature = "alloc", not(verus_verify_core)))]
311impl DeepView for String {
312    type V = Seq<char>;
313
314    open spec fn deep_view(&self) -> Seq<char> {
315        self.view()
316    }
317}
318
319#[cfg(all(feature = "alloc", not(verus_verify_core)))]
320#[verifier::external_type_specification]
321#[verifier::external_body]
322pub struct ExString(String);
323
324#[cfg(all(feature = "alloc", not(verus_verify_core)))]
325pub open spec fn string_is_ascii(s: &String) -> bool {
326    is_ascii_chars(s@)
327}
328
329#[cfg(all(feature = "alloc", not(verus_verify_core)))]
330pub assume_specification<'a>[ String::as_str ](s: &'a String) -> (res: &'a str)
331    ensures
332        res@ == s@,
333;
334
335// same as above
336#[cfg(all(feature = "alloc", not(verus_verify_core)))]
337pub assume_specification<'a>[ <String as core::ops::Deref>::deref ](s: &'a String) -> (res: &'a str)
338    ensures
339        res@ == s@,
340;
341
342#[cfg(all(feature = "alloc", not(verus_verify_core)))]
343pub assume_specification[ <String as Clone>::clone ](s: &String) -> (res: String)
344    ensures
345        res == s,
346;
347
348#[cfg(all(feature = "alloc", not(verus_verify_core)))]
349pub assume_specification[ <String as PartialEq>::eq ](s: &String, other: &String) -> (res: bool)
350    ensures
351        res == (s@ == other@),
352;
353
354#[cfg(all(feature = "alloc", not(verus_verify_core)))]
355pub assume_specification[ String::new ]() -> (res: String)
356    ensures
357        res@ == Seq::<char>::empty(),
358;
359
360#[cfg(all(feature = "alloc", not(verus_verify_core)))]
361pub assume_specification[ <String as core::default::Default>::default ]() -> (r: String)
362    ensures
363        r@ == Seq::<char>::empty(),
364;
365
366#[cfg(all(feature = "alloc", not(verus_verify_core)))]
367pub trait StringExecFnsIsAscii: Sized {
368    fn is_ascii(&self) -> bool;
369}
370
371#[cfg(all(feature = "alloc", not(verus_verify_core)))]
372impl StringExecFnsIsAscii for String {
373    #[inline(always)]
374    #[verifier::when_used_as_spec(string_is_ascii)]
375    fn is_ascii(&self) -> (ret: bool)
376        ensures
377            ret == string_is_ascii(self),
378    {
379        self.as_str().is_ascii()
380    }
381}
382
383#[cfg(all(feature = "alloc", not(verus_verify_core)))]
384#[verifier::external]
385pub trait StringExecFns: Sized {
386    fn from_str<'a>(s: &'a str) -> String;
387
388    fn append<'a, 'b>(&'a mut self, other: &'b str);
389
390    fn concat<'b>(self, other: &'b str) -> String;
391}
392
393#[cfg(all(feature = "alloc", not(verus_verify_core)))]
394impl StringExecFns for String {
395    #[verifier::external_body]
396    fn from_str<'a>(s: &'a str) -> (ret: String)
397        ensures
398            s@ == ret@,
399    {
400        s.to_string()
401    }
402
403    #[verifier::external_body]
404    fn append<'a, 'b>(&'a mut self, other: &'b str)
405        ensures
406            self@ == old(self)@ + other@,
407    {
408        *self += other;
409    }
410
411    #[verifier::external_body]
412    fn concat<'b>(self, other: &'b str) -> (ret: String)
413        ensures
414            ret@ == self@ + other@,
415    {
416        self + other
417    }
418}
419
420#[cfg(all(feature = "alloc", not(verus_verify_core)))]
421pub assume_specification[ str::chars ](s: &str) -> (chars: Chars<'_>)
422    ensures
423        ({
424            let (index, c) = chars@;
425            &&& index == 0
426            &&& c == s@
427        }),
428;
429
430// The `chars` method of a `str` returns an iterator of type `Chars`,
431// so we specify that type here.
432#[verifier::external_type_specification]
433#[verifier::external_body]
434#[cfg(all(feature = "alloc", not(verus_verify_core)))]
435pub struct ExChars<'a>(Chars<'a>);
436
437#[cfg(all(feature = "alloc", not(verus_verify_core)))]
438impl<'a> View for Chars<'a> {
439    type V = (int, Seq<char>);
440
441    uninterp spec fn view(&self) -> (int, Seq<char>);
442}
443
444#[cfg(all(feature = "alloc", not(verus_verify_core)))]
445impl<'a> DeepView for Chars<'a> {
446    type V = <Self as View>::V;
447
448    open spec fn deep_view(&self) -> Self::V {
449        self@
450    }
451}
452
453#[cfg(all(feature = "alloc", not(verus_verify_core)))]
454pub assume_specification<'a>[ Chars::<'a>::next ](chars: &mut Chars<'a>) -> (r: Option<char>)
455    ensures
456        ({
457            let (old_index, old_seq) = old(chars)@;
458            match r {
459                None => {
460                    &&& chars@ == old(chars)@
461                    &&& old_index >= old_seq.len()
462                },
463                Some(k) => {
464                    let (new_index, new_seq) = chars@;
465                    &&& 0 <= old_index < old_seq.len()
466                    &&& new_seq == old_seq
467                    &&& new_index == old_index + 1
468                    &&& k == old_seq[old_index]
469                },
470            }
471        }),
472;
473
474#[cfg(all(feature = "alloc", not(verus_verify_core)))]
475pub struct CharsGhostIterator<'a> {
476    pub pos: int,
477    pub chars: Seq<char>,
478    pub phantom: Option<&'a char>,
479}
480
481#[cfg(all(feature = "alloc", not(verus_verify_core)))]
482impl<'a> ForLoopGhostIteratorNew for Chars<'a> {
483    type GhostIter = CharsGhostIterator<'a>;
484
485    open spec fn ghost_iter(&self) -> CharsGhostIterator<'a> {
486        CharsGhostIterator { pos: self@.0, chars: self@.1, phantom: None }
487    }
488}
489
490#[cfg(all(feature = "alloc", not(verus_verify_core)))]
491impl<'a> ForLoopGhostIterator for CharsGhostIterator<'a> {
492    type ExecIter = Chars<'a>;
493
494    type Item = char;
495
496    type Decrease = int;
497
498    open spec fn exec_invariant(&self, exec_iter: &Chars<'a>) -> bool {
499        &&& self.pos == exec_iter@.0
500        &&& self.chars == exec_iter@.1
501    }
502
503    open spec fn ghost_invariant(&self, init: Option<&Self>) -> bool {
504        init matches Some(init) ==> {
505            &&& init.pos == 0
506            &&& init.chars == self.chars
507            &&& 0 <= self.pos <= self.chars.len()
508        }
509    }
510
511    open spec fn ghost_ensures(&self) -> bool {
512        self.pos == self.chars.len()
513    }
514
515    open spec fn ghost_decrease(&self) -> Option<int> {
516        Some(self.chars.len() - self.pos)
517    }
518
519    open spec fn ghost_peek_next(&self) -> Option<char> {
520        if 0 <= self.pos < self.chars.len() {
521            Some(self.chars[self.pos])
522        } else {
523            None
524        }
525    }
526
527    open spec fn ghost_advance(&self, _exec_iter: &Chars<'a>) -> CharsGhostIterator<'a> {
528        Self { pos: self.pos + 1, ..*self }
529    }
530}
531
532#[cfg(all(feature = "alloc", not(verus_verify_core)))]
533impl<'a> View for CharsGhostIterator<'a> {
534    type V = Seq<char>;
535
536    open spec fn view(&self) -> Seq<char> {
537        self.chars.take(self.pos)
538    }
539}
540
541#[cfg(all(feature = "alloc", not(verus_verify_core)))]
542impl<'a> DeepView for CharsGhostIterator<'a> {
543    type V = Seq<char>;
544
545    open spec fn deep_view(&self) -> Seq<char> {
546        self.view()
547    }
548}
549
550pub use super::view::View;
551
552} // verus!