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