Skip to main content

vstd/
hash_map.rs

1#[cfg(verus_keep_ghost)]
2use super::laws_eq::obeys_view_eq;
3#[allow(unused_imports)]
4use super::map::*;
5#[allow(unused_imports)]
6use super::pervasive::*;
7use super::prelude::*;
8#[cfg(verus_keep_ghost)]
9use super::std_specs::borrow::BorrowSpec;
10#[cfg(verus_keep_ghost)]
11use super::std_specs::cmp::PartialEqSpec;
12#[cfg(verus_keep_ghost)]
13use super::std_specs::hash::{hash_map_inv, obeys_key_model};
14#[cfg(verus_keep_ghost)]
15use super::std_specs::iter::IteratorSpec;
16#[allow(unused_imports)]
17use core::hash::Hash;
18use std::collections::HashMap;
19
20verus! {
21
22broadcast use {
23    crate::map::group_map_lemmas,
24    crate::set::group_set_lemmas,
25    crate::set::Set::lemma_map_contains,
26    crate::seq::group_seq_axioms,
27    crate::seq_lib::lemma_seq_contains,
28    crate::seq_lib::group_seq_lib_default,
29    crate::std_specs::hash::group_hash_axioms,
30    crate::std_specs::borrow::group_borrow_axioms,
31};
32
33/// `HashMapWithView` is a verified wrapper around `std::collections::HashMap` with `View` implemented for the type `vstd::map::Map<<Key as View>::V, Value>`.
34///
35/// Its constructors require [`obeys_view_eq`] in addition to [`obeys_key_model`].
36/// Constructors establish [`Self::well_formed`], which all other operations require and preserve.
37///
38/// See the Rust documentation for [`HashMap`](https://doc.rust-lang.org/std/collections/struct.HashMap.html)
39/// for details about its implementation.
40///
41/// If you are using `std::collections::HashMap` directly, see [`ExHashMap`](https://verus-lang.github.io/verus/verusdoc/vstd/std_specs/hash/struct.ExHashMap.html)
42/// for information on the Verus specifications for this type.
43#[verifier::ext_equal]
44#[verifier::reject_recursive_types(Key)]
45#[verifier::reject_recursive_types(Value)]
46pub struct HashMapWithView<Key, Value> where Key: View + Eq + Hash {
47    m: HashMap<Key, Value>,
48}
49
50impl<Key, Value> View for HashMapWithView<Key, Value> where Key: View + Eq + Hash {
51    type V = Map<<Key as View>::V, Value>;
52
53    closed spec fn view(&self) -> Self::V {
54        Map::new(
55            self.m@.dom().map(|k: Key| k@),
56            |view: Key::V| self.m@[choose|k: Key| #[trigger] self.m@.contains_key(k) && k@ == view],
57        )
58    }
59}
60
61impl<Key, Value> HashMapWithView<Key, Value> where Key: View + Eq + Hash {
62    pub closed spec fn well_formed(&self) -> bool {
63        &&& obeys_key_model::<Key>()
64        &&& obeys_view_eq::<Key>()
65        &&& hash_map_inv(&self.m)
66    }
67
68    proof fn lemma_view(&self)
69        requires
70            self.well_formed(),
71        ensures
72            self@.len() == self.m@.len(),
73            forall|k: Key| #[trigger]
74                self.m@.contains_key(k) ==> self@.contains_key(k@) && self@[k@] == self.m@[k],
75            forall|view: Key::V| #[trigger]
76                self@.contains_key(view) ==> exists|k: Key| #[trigger]
77                    self.m@.contains_key(k) && k@ == view,
78    {
79        reveal(obeys_view_eq);
80        broadcast use {crate::map::group_map_lemmas, crate::set::group_set_lemmas};
81
82        assert(self.m@.dom().injective_on(|k: Key| k@));
83        crate::set_lib::lemma_map_size(self.m@.dom(), self.m@.dom().map(|k: Key| k@), |k: Key| k@);
84        assert forall|k: Key| #[trigger] self.m@.contains_key(k) implies self@.contains_key(k@)
85            && self@[k@] == self.m@[k] by {
86            let other = choose|other: Key| #[trigger] self.m@.contains_key(other) && other@ == k@;
87            assert(other == k);
88        }
89    }
90
91    /// Creates an empty `HashMapWithView` with a capacity of 0.
92    ///
93    /// See [`obeys_key_model()`](https://verus-lang.github.io/verus/verusdoc/vstd/std_specs/hash/fn.obeys_key_model.html)
94    /// for information on use with primitive types and other types.
95    /// See Rust's [`HashMap::new()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.new) for implementation details.
96    pub fn new() -> (result: Self)
97        requires
98            obeys_key_model::<Key>(),
99            obeys_view_eq::<Key>(),
100        ensures
101            result.well_formed(),
102            result@ == Map::<<Key as View>::V, Value>::empty(),
103    {
104        Self { m: HashMap::new() }
105    }
106
107    /// Creates an empty `HashMapWithView` with at least capacity for the specified number of elements.
108    ///
109    /// See [`obeys_key_model()`](https://verus-lang.github.io/verus/verusdoc/vstd/std_specs/hash/fn.obeys_key_model.html)
110    /// for information on use with primitive types and other types.
111    /// See Rust's [`HashMap::with_capacity()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.with_capacity) for implementation details.
112    pub fn with_capacity(capacity: usize) -> (result: Self)
113        requires
114            obeys_key_model::<Key>(),
115            obeys_view_eq::<Key>(),
116        ensures
117            result.well_formed(),
118            result@ == Map::<<Key as View>::V, Value>::empty(),
119    {
120        Self { m: HashMap::with_capacity(capacity) }
121    }
122
123    /// Reserves capacity for at least `additional` number of elements in the map.
124    ///
125    /// See Rust's [`HashMap::reserve()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.reserve) for implementation details.
126    pub fn reserve(&mut self, additional: usize)
127        requires
128            old(self).well_formed(),
129        ensures
130            final(self).well_formed(),
131            final(self)@ == old(self)@,
132    {
133        self.m.reserve(additional)
134    }
135
136    /// Returns true if the map is empty.
137    pub fn is_empty(&self) -> (result: bool)
138        requires
139            self.well_formed(),
140        ensures
141            result == self@.is_empty(),
142    {
143        proof {
144            self.lemma_view();
145        }
146        self.m.is_empty()
147    }
148
149    /// Returns the number of elements in the map.
150    pub open spec fn spec_len(&self) -> usize {
151        self@.len() as usize
152    }
153
154    /// Returns the number of elements in the map.
155    #[verifier::when_used_as_spec(spec_len)]
156    pub fn len(&self) -> (result: usize)
157        requires
158            self.well_formed(),
159        ensures
160            result == self@.len(),
161    {
162        proof {
163            self.lemma_view();
164        }
165        self.m.len()
166    }
167
168    /// Inserts the given key and value in the map, returning the previous value if the key was present.
169    ///
170    /// See Rust's [`HashMap::insert()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.insert) for implementation details.
171    pub fn insert(&mut self, k: Key, v: Value) -> (previous: Option<Value>)
172        requires
173            old(self).well_formed(),
174        ensures
175            final(self).well_formed(),
176            final(self)@ == old(self)@.insert(k@, v),
177            previous == old(self)@.get(k@),
178    {
179        proof {
180            reveal(obeys_view_eq);
181            self.lemma_view();
182        }
183        let ghost before = self@;
184        let ghost old_m = self.m@;
185        let previous = self.m.insert(k, v);
186        proof {
187            self.lemma_view();
188            let stored_key = match &previous {
189                Some(old_value) => choose|stored: Key|
190                    {
191                        &&& #[trigger] old_m.contains_key(stored)
192                        &&& k.eq_spec(&stored)
193                        &&& *old_value == old_m[stored]
194                        &&& self.m@ == old_m.insert(stored, v)
195                    },
196                None => k,
197            };
198            assert(stored_key@ == k@);
199            assert(self.m@.contains_key(stored_key));
200            assert_maps_equal!(self@, before.insert(k@, v), view => {
201                if view == k@ {
202                    assert(self@.contains_key(view));
203                    assert(self@[view] == v);
204                } else if self@.contains_key(view) {
205                    let stored = choose|stored: Key|
206                        #[trigger] self.m@.contains_key(stored) && stored@ == view;
207                    assert(old_m.contains_key(stored));
208                    assert(before.contains_key(view));
209                    assert(self@[view] == before[view]);
210                }
211                if before.contains_key(view) && view != k@ {
212                    let stored = choose|stored: Key|
213                        #[trigger] old_m.contains_key(stored) && stored@ == view;
214                    assert(self.m@.contains_key(stored));
215                    assert(self@.contains_key(view));
216                }
217            });
218        }
219        previous
220    }
221
222    /// Removes the given key from the map and returns the value. If the key is not present in the map, returns `None`
223    /// and the map is unmodified.
224    ///
225    /// See Rust's [`HashMap::remove()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.remove) for implementation details.
226    pub fn remove(&mut self, k: &Key) -> (out: Option<Value>)
227        requires
228            old(self).well_formed(),
229        ensures
230            final(self).well_formed(),
231            match out {
232                Some(v) => old(self)@.contains_key(k@) && v == old(self)@[k@] && final(self)@
233                    == old(self)@.remove(k@),
234                None => !old(self)@.contains_key(k@) && final(self)@ == old(self)@,
235            },
236    {
237        proof {
238            reveal(obeys_view_eq);
239            self.lemma_view();
240        }
241        let ghost before = self@;
242        let ghost old_m = self.m@;
243        let out = self.m.remove(k);
244        proof {
245            self.lemma_view();
246            if out is Some {
247                assert_maps_equal!(self@, before.remove(k@), view => {
248                    if self@.contains_key(view) {
249                        let stored = choose|stored: Key|
250                            #[trigger] self.m@.contains_key(stored) && stored@ == view;
251                        assert(view != k@);
252                    }
253                    if before.contains_key(view) && view != k@ {
254                        let stored = choose|stored: Key|
255                            #[trigger] old_m.contains_key(stored) && stored@ == view;
256                        assert(self.m@.contains_key(stored));
257                    }
258                });
259            }
260        }
261        out
262    }
263
264    /// Returns true if the map contains the given key.
265    ///
266    /// See Rust's [`HashMap::contains_key()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.contains_key) for implementation details.
267    pub fn contains_key(&self, k: &Key) -> (result: bool)
268        requires
269            self.well_formed(),
270        ensures
271            result == self@.contains_key(k@),
272    {
273        proof {
274            reveal(obeys_view_eq);
275            self.lemma_view();
276        }
277        self.m.contains_key(k)
278    }
279
280    /// Returns a reference to the value corresponding to the given key in the map. If the key is not present in the map, returns `None`.
281    ///
282    /// See Rust's [`HashMap::get()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.get) for implementation details.
283    pub fn get<'a>(&'a self, k: &Key) -> (result: Option<&'a Value>)
284        requires
285            self.well_formed(),
286        ensures
287            match result {
288                Some(v) => self@.contains_key(k@) && *v == self@[k@],
289                None => !self@.contains_key(k@),
290            },
291    {
292        proof {
293            reveal(obeys_view_eq);
294            self.lemma_view();
295        }
296        self.m.get(k)
297    }
298
299    /// Clears all key-value pairs in the map. Retains the allocated memory for reuse.
300    ///
301    /// See Rust's [`HashMap::clear()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.clear) for implementation details.
302    pub fn clear(&mut self)
303        requires
304            old(self).well_formed(),
305        ensures
306            final(self).well_formed(),
307            final(self)@ == Map::<<Key as View>::V, Value>::empty(),
308    {
309        self.m.clear();
310    }
311
312    /// Returns the union of the two maps. If a key is present in both maps, then the value in the right map (`other`) is retained.
313    pub fn union_prefer_right(&mut self, other: Self)
314        requires
315            old(self).well_formed(),
316            other.well_formed(),
317        ensures
318            final(self).well_formed(),
319            final(self)@ == old(self)@.union_prefer_right(other@),
320    {
321        proof {
322            self.lemma_view();
323            other.lemma_view();
324        }
325        let ghost target = self@.union_prefer_right(other@);
326        let ghost other_view = other@;
327        let ghost other_map = other.m@;
328        let mut iter = other.m.into_iter();
329        proof {
330            assert(iter.remaining().to_set() == other_map.kv_pairs());
331            assert forall|i: int| 0 <= i < iter.remaining().len() implies other_view.contains_key(
332                #[trigger] iter.remaining()[i].0@,
333            ) && other_view[iter.remaining()[i].0@] == iter.remaining()[i].1 by {
334                let pair = iter.remaining()[i];
335                assert(iter.remaining().to_set().contains(pair));
336                assert(other_map.contains_key(pair.0));
337            }
338            assert forall|view: Key::V| #[trigger] target.contains_key(view) implies (
339            self@.contains_key(view) && self@[view] == target[view]) || exists|i: int|
340                0 <= i < iter.remaining().len() && #[trigger] iter.remaining()[i].0@ == view by {
341                if other_view.contains_key(view) {
342                    let key = choose|key: Key| #[trigger]
343                        other_map.contains_key(key) && key@ == view;
344                    assert(other_map.kv_pairs().contains((key, other_map[key])));
345                    assert(iter.remaining().to_set().contains((key, other_map[key])));
346                    assert(iter.remaining().contains((key, other_map[key])));
347                    let i = choose|i: int|
348                        0 <= i < iter.remaining().len() && #[trigger] iter.remaining()[i] == (
349                            key,
350                            other_map[key],
351                        );
352                    assert(iter.remaining()[i].0@ == view);
353                }
354            }
355        }
356        loop
357            invariant
358                self.well_formed(),
359                iter.decrease() is Some,
360                target == old(self)@.union_prefer_right(other@),
361                other_view.submap_of(target),
362                forall|view: Key::V| #[trigger]
363                    self@.contains_key(view) ==> target.contains_key(view),
364                forall|view: Key::V| #[trigger]
365                    target.contains_key(view) ==> (self@.contains_key(view) && self@[view]
366                        == target[view]) || exists|i: int|
367                        0 <= i < iter.remaining().len() && #[trigger] iter.remaining()[i].0@
368                            == view,
369                forall|i: int|
370                    0 <= i < iter.remaining().len() ==> other_view.contains_key(
371                        #[trigger] iter.remaining()[i].0@,
372                    ) && other_view[iter.remaining()[i].0@] == iter.remaining()[i].1,
373            decreases iter.decrease()->0,
374        {
375            #[verifier::prophetic]
376            let ghost remaining = iter.remaining();
377            let ghost before = self@;
378            match iter.next() {
379                Some((key, value)) => {
380                    self.insert(key, value);
381                    proof {
382                        assert forall|view: Key::V| #[trigger] target.contains_key(view) implies (
383                        self@.contains_key(view) && self@[view] == target[view]) || exists|i: int|
384                            0 <= i < iter.remaining().len() && #[trigger] iter.remaining()[i].0@
385                                == view by {
386                            if !(self@.contains_key(view) && self@[view] == target[view]) {
387                                assert(!(before.contains_key(view) && before[view]
388                                    == target[view]));
389                                let i = choose|i: int|
390                                    0 <= i < remaining.len() && #[trigger] remaining[i].0@ == view;
391                                assert(i > 0);
392                                assert(iter.remaining()[i - 1].0@ == view);
393                            }
394                        }
395                    }
396                },
397                None => {
398                    proof {
399                        assert(iter.remaining().len() == 0);
400                        assert(self@ =~= target);
401                    }
402                    return;
403                },
404            }
405        }
406    }
407}
408
409/// `StringHashMap` is a verified wrapper around `std::collections::HashMap<String, Value>` with `View` implemented for the type `vstd::map::Map<Seq<char>, Value>`.
410///
411/// This type was created for ease of use with `String` as it uses `&str` instead of `&String` for methods that require shared references.
412/// Constructors establish [`Self::well_formed`], which all other operations require and preserve.
413/// Standard-library axioms provide the key models for `String` and `str`.
414///
415/// See the Rust documentation for [`HashMap`](https://doc.rust-lang.org/std/collections/struct.HashMap.html)
416/// for details about its implementation.
417///
418/// If you are using `std::collections::HashMap` directly, see [`ExHashMap`](https://verus-lang.github.io/verus/verusdoc/vstd/std_specs/hash/struct.ExHashMap.html)
419/// for information on the Verus specifications for this type.
420#[verifier::ext_equal]
421#[verifier::reject_recursive_types(Value)]
422pub struct StringHashMap<Value> {
423    m: HashMapWithView<String, Value>,
424}
425
426impl<Value> View for StringHashMap<Value> {
427    type V = Map<Seq<char>, Value>;
428
429    closed spec fn view(&self) -> Self::V {
430        self.m@
431    }
432}
433
434impl<Value> StringHashMap<Value> {
435    pub closed spec fn well_formed(&self) -> bool {
436        self.m.well_formed()
437    }
438
439    /// Creates an empty `StringHashMap` with a capacity of 0.
440    ///
441    /// See Rust's [`HashMap::new()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.new) for implementation details.
442    pub fn new() -> (result: Self)
443        ensures
444            result.well_formed(),
445            result@ == Map::<Seq<char>, Value>::empty(),
446    {
447        proof {
448            reveal(obeys_view_eq);
449        }
450        Self { m: HashMapWithView::new() }
451    }
452
453    /// Creates an empty `StringHashMap` with at least capacity for the specified number of elements.
454    ///
455    /// See Rust's [`HashMap::with_capacity()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.with_capacity) for implementation details.
456    pub fn with_capacity(capacity: usize) -> (result: Self)
457        ensures
458            result.well_formed(),
459            result@ == Map::<Seq<char>, Value>::empty(),
460    {
461        proof {
462            reveal(obeys_view_eq);
463        }
464        Self { m: HashMapWithView::with_capacity(capacity) }
465    }
466
467    /// Reserves capacity for at least `additional` number of elements in the map.
468    ///
469    /// See Rust's [`HashMap::reserve()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.reserve) for implementation details.
470    pub fn reserve(&mut self, additional: usize)
471        requires
472            old(self).well_formed(),
473        ensures
474            final(self).well_formed(),
475            final(self)@ == old(self)@,
476    {
477        self.m.reserve(additional);
478    }
479
480    /// Returns true if the map is empty.
481    pub fn is_empty(&self) -> (result: bool)
482        requires
483            self.well_formed(),
484        ensures
485            result == self@.is_empty(),
486    {
487        self.m.is_empty()
488    }
489
490    /// Returns the number of elements in the map.
491    pub open spec fn spec_len(&self) -> usize {
492        self@.len() as usize
493    }
494
495    /// Returns the number of elements in the map.
496    #[verifier::when_used_as_spec(spec_len)]
497    pub fn len(&self) -> (result: usize)
498        requires
499            self.well_formed(),
500        ensures
501            result == self@.len(),
502    {
503        self.m.len()
504    }
505
506    /// Inserts the given key and value in the map, returning the previous value if the key was present.
507    ///
508    /// See Rust's [`HashMap::insert()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.insert) for implementation details.
509    pub fn insert(&mut self, k: String, v: Value) -> (previous: Option<Value>)
510        requires
511            old(self).well_formed(),
512        ensures
513            final(self).well_formed(),
514            final(self)@ == old(self)@.insert(k@, v),
515            previous == old(self)@.get(k@),
516    {
517        self.m.insert(k, v)
518    }
519
520    /// Removes the given key from the map. If the key is not present in the map, the map is unmodified.
521    ///
522    /// See Rust's [`HashMap::remove()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.remove) for implementation details.
523    pub fn remove(&mut self, k: &str)
524        requires
525            old(self).well_formed(),
526        ensures
527            final(self).well_formed(),
528            final(self)@ == old(self)@.remove(k@),
529    {
530        proof {
531            self.m.lemma_view();
532        }
533        let ghost before = self@;
534        let ghost old_m = self.m.m@;
535        let out = self.m.m.remove(k);
536        proof {
537            self.m.lemma_view();
538            if out is None && before.contains_key(k@) {
539                let stored = choose|stored: String| #[trigger]
540                    old_m.contains_key(stored) && stored@ == k@;
541                assert(k.eq_spec(<String as BorrowSpec<str>>::borrow_spec(&stored)));
542            }
543            assert_maps_equal!(self@, before.remove(k@), view => {
544                if self@.contains_key(view) {
545                    let stored = choose|stored: String| #[trigger]
546                        self.m.m@.contains_key(stored) && stored@ == view;
547                    assert(view != k@);
548                }
549                if before.contains_key(view) && view != k@ {
550                    let stored = choose|stored: String| #[trigger]
551                        old_m.contains_key(stored) && stored@ == view;
552                    assert(self.m.m@.contains_key(stored));
553                }
554            });
555        }
556    }
557
558    /// Returns true if the map contains the given key.
559    ///
560    /// See Rust's [`HashMap::contains_key()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.contains_key) for implementation details.
561    pub fn contains_key(&self, k: &str) -> (result: bool)
562        requires
563            self.well_formed(),
564        ensures
565            result == self@.contains_key(k@),
566    {
567        self.get(k).is_some()
568    }
569
570    /// Returns a reference to the value corresponding to the given key in the map. If the key is not present in the map, returns `None`.
571    ///
572    /// See Rust's [`HashMap::get()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.get) for implementation details.
573    pub fn get<'a>(&'a self, k: &str) -> (result: Option<&'a Value>)
574        requires
575            self.well_formed(),
576        ensures
577            match result {
578                Some(v) => self@.contains_key(k@) && *v == self@[k@],
579                None => !self@.contains_key(k@),
580            },
581    {
582        proof {
583            self.m.lemma_view();
584        }
585        let result = self.m.m.get(k);
586        proof {
587            match result {
588                Some(value) => {
589                    let stored = choose|stored: String|
590                        {
591                            &&& #[trigger] self.m.m@.contains_key(stored)
592                            &&& k.eq_spec(<String as BorrowSpec<str>>::borrow_spec(&stored))
593                            &&& self.m.m@[stored] == *value
594                        };
595                    assert(stored@ == k@);
596                },
597                None => {
598                    if self@.contains_key(k@) {
599                        let stored = choose|stored: String| #[trigger]
600                            self.m.m@.contains_key(stored) && stored@ == k@;
601                        assert(k.eq_spec(<String as BorrowSpec<str>>::borrow_spec(&stored)));
602                    }
603                },
604            }
605        }
606        result
607    }
608
609    /// Clears all key-value pairs in the map. Retains the allocated memory for reuse.
610    ///
611    /// See Rust's [`HashMap::clear()`](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.clear) for implementation details.
612    pub fn clear(&mut self)
613        requires
614            old(self).well_formed(),
615        ensures
616            final(self).well_formed(),
617            final(self)@ == Map::<Seq<char>, Value>::empty(),
618    {
619        self.m.clear()
620    }
621
622    /// Returns the union of the two maps. If a key is present in both maps, then the value in the right map (`other`) is retained.
623    pub fn union_prefer_right(&mut self, other: Self)
624        requires
625            old(self).well_formed(),
626            other.well_formed(),
627        ensures
628            final(self).well_formed(),
629            final(self)@ == old(self)@.union_prefer_right(other@),
630    {
631        self.m.union_prefer_right(other.m)
632    }
633}
634
635} // verus!