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    is_ascii_spec_bytes,
302    is_ascii_concat,
303}
304
305#[cfg(all(feature = "alloc", not(verus_verify_core)))]
306impl View for String {
307    type V = Seq<char>;
308
309    uninterp spec fn view(&self) -> Seq<char>;
310}
311
312#[cfg(all(feature = "alloc", not(verus_verify_core)))]
313impl DeepView for String {
314    type V = Seq<char>;
315
316    open spec fn deep_view(&self) -> Seq<char> {
317        self.view()
318    }
319}
320
321#[cfg(all(feature = "alloc", not(verus_verify_core)))]
322#[verifier::external_type_specification]
323#[verifier::external_body]
324pub struct ExString(String);
325
326#[cfg(all(feature = "alloc", not(verus_verify_core)))]
327pub open spec fn string_is_ascii(s: &String) -> bool {
328    is_ascii_chars(s@)
329}
330
331#[cfg(all(feature = "alloc", not(verus_verify_core)))]
332pub assume_specification<'a>[ String::as_str ](s: &'a String) -> (res: &'a str)
333    ensures
334        res@ == s@,
335;
336
337// same as above
338#[cfg(all(feature = "alloc", not(verus_verify_core)))]
339pub assume_specification<'a>[ <String as core::ops::Deref>::deref ](s: &'a String) -> (res: &'a str)
340    ensures
341        res@ == s@,
342;
343
344#[cfg(all(feature = "alloc", not(verus_verify_core)))]
345pub assume_specification[ <String as Clone>::clone ](s: &String) -> (res: String)
346    ensures
347        res == s,
348;
349
350#[cfg(all(feature = "alloc", not(verus_verify_core)))]
351pub assume_specification[ <String as PartialEq>::eq ](s: &String, other: &String) -> (res: bool)
352    ensures
353        res == (s@ == other@),
354;
355
356#[cfg(all(feature = "alloc", not(verus_verify_core)))]
357pub assume_specification[ String::new ]() -> (res: String)
358    ensures
359        res@ == Seq::<char>::empty(),
360;
361
362#[cfg(all(feature = "alloc", not(verus_verify_core)))]
363pub assume_specification[ <String as core::default::Default>::default ]() -> (r: String)
364    ensures
365        r@ == Seq::<char>::empty(),
366;
367
368#[cfg(all(feature = "alloc", not(verus_verify_core)))]
369pub trait StringExecFnsIsAscii: Sized {
370    fn is_ascii(&self) -> bool;
371}
372
373#[cfg(all(feature = "alloc", not(verus_verify_core)))]
374impl StringExecFnsIsAscii for String {
375    #[inline(always)]
376    #[verifier::when_used_as_spec(string_is_ascii)]
377    fn is_ascii(&self) -> (ret: bool)
378        ensures
379            ret == string_is_ascii(self),
380    {
381        self.as_str().is_ascii()
382    }
383}
384
385#[cfg(all(feature = "alloc", not(verus_verify_core)))]
386#[verifier::external]
387pub trait StringExecFns: Sized {
388    fn from_str<'a>(s: &'a str) -> String;
389
390    fn append<'a, 'b>(&'a mut self, other: &'b str);
391
392    fn concat<'b>(self, other: &'b str) -> String;
393}
394
395#[cfg(all(feature = "alloc", not(verus_verify_core)))]
396impl StringExecFns for String {
397    #[verifier::external_body]
398    fn from_str<'a>(s: &'a str) -> (ret: String)
399        ensures
400            s@ == ret@,
401    {
402        s.to_string()
403    }
404
405    #[verifier::external_body]
406    fn append<'a, 'b>(&'a mut self, other: &'b str)
407        ensures
408            final(self)@ == old(self)@ + other@,
409    {
410        *self += other;
411    }
412
413    #[verifier::external_body]
414    fn concat<'b>(self, other: &'b str) -> (ret: String)
415        ensures
416            ret@ == self@ + other@,
417    {
418        self + other
419    }
420}
421
422// The `chars` method of a `str` returns an iterator of type `Chars`,
423// so we specify that type here.
424#[verifier::external_type_specification]
425#[verifier::external_body]
426#[cfg(all(feature = "alloc", not(verus_verify_core)))]
427pub struct ExChars<'a>(Chars<'a>);
428
429// To allow reasoning about the "contents" of the string iterator, without using
430// a prophecy, we need a function that gives us the underlying sequence of the original string.
431#[cfg(feature = "alloc")]
432pub uninterp spec fn into_iter_elts<'a>(i: Chars<'a>) -> Seq<char>;
433
434// To allow reasoning about the ghost iterator when the executable
435// function `iter()` is invoked in a `for` loop header (e.g., in
436// `for x in it: v.iter() { ... }`), we need to specify the behavior of
437// the iterator in spec mode. To do that, we add
438// `#[verifier::when_used_as_spec(spec_iter)` to the specification for
439// the executable `iter` method and define that spec function here.
440#[cfg(feature = "alloc")]
441pub uninterp spec fn spec_iter<'a>(s: &'a str) -> (r: Chars<'a>);
442
443#[cfg(feature = "alloc")]
444pub broadcast proof fn axiom_spec_iter<'a>(s: &'a str)
445    ensures
446        #[trigger] spec_iter(s).remaining() == s@,
447{
448    admit();
449}
450
451#[cfg(feature = "alloc")]
452pub assume_specification[ str::chars ](s: &str) -> (iter: Chars<'_>)
453    ensures
454        iter == spec_iter(s),
455        IteratorSpec::decrease(&iter) is Some,
456        IteratorSpec::initial_value_relation(&iter, &iter),
457;
458
459#[cfg(verus_keep_ghost)]
460#[cfg(feature = "alloc")]
461impl<'a> super::std_specs::iter::IteratorSpecImpl for Chars<'a> {
462    open spec fn obeys_prophetic_iter_laws(&self) -> bool {
463        true
464    }
465
466    uninterp spec fn remaining(&self) -> Seq<Self::Item>;
467
468    uninterp spec fn will_return_none(&self) -> bool;
469
470    #[verifier::prophetic]
471    open spec fn initial_value_relation(&self, init: &Self) -> bool {
472        &&& IteratorSpec::remaining(init) == IteratorSpec::remaining(self)
473        &&& into_iter_elts(*self) == IteratorSpec::remaining(self)
474    }
475
476    uninterp spec fn decrease(&self) -> Option<nat>;
477
478    open spec fn peek(&self, index: int) -> Option<Self::Item> {
479        if 0 <= index < into_iter_elts(*self).len() {
480            Some(into_iter_elts(*self)[index])
481        } else {
482            None
483        }
484    }
485}
486
487pub use super::view::View;
488
489} // verus!