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 #[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 #[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 &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 &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 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#[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#[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#[cfg(feature = "alloc")]
432pub uninterp spec fn into_iter_elts<'a>(i: Chars<'a>) -> Seq<char>;
433
434#[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}