Skip to main content

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