/src/wasmtime/cranelift/codegen/src/scoped_hash_map.rs
Line | Count | Source |
1 | | //! `ScopedHashMap` |
2 | | //! |
3 | | //! This module defines a struct `ScopedHashMap<C, K, V>` which |
4 | | //! defines a `FxHashMap`-like container that has a concept of scopes |
5 | | //! that can be entered and exited, such that values inserted while |
6 | | //! inside a scope aren't visible outside the scope. |
7 | | //! |
8 | | //! The context type `C` is given to `CtxEq` and `CtxHash` methods on |
9 | | //! the key values so that keys do not need to be fully |
10 | | //! self-contained. |
11 | | |
12 | | use crate::ctxhash::{CtxEq, CtxHash, CtxHashMap}; |
13 | | use smallvec::{SmallVec, smallvec}; |
14 | | |
15 | | struct Val<V> { |
16 | | value: V, |
17 | | level: u32, |
18 | | generation: u32, |
19 | | } |
20 | | |
21 | | /// A view into an occupied entry in a `ScopedHashMap`. It is part of the `Entry` enum. |
22 | | pub struct OccupiedEntry<'a, K: 'a, V: 'a> { |
23 | | entry: crate::ctxhash::OccupiedEntry<'a, K, Val<V>>, |
24 | | } |
25 | | |
26 | | impl<'a, K, V> OccupiedEntry<'a, K, V> { |
27 | | /// Gets a reference to the value in the entry. |
28 | 56.2k | pub fn get(&self) -> &V { |
29 | 56.2k | &self.entry.get().value |
30 | 56.2k | } |
31 | | } |
32 | | |
33 | | /// A view into a vacant entry in a `ScopedHashMap`. It is part of the `Entry` enum. |
34 | | pub struct VacantEntry<'a, K: 'a, V: 'a> { |
35 | | entry: InsertLoc<'a, K, V>, |
36 | | depth: u32, |
37 | | generation: u32, |
38 | | } |
39 | | |
40 | | /// Where to insert from a `VacantEntry`. May be vacant or occupied in |
41 | | /// the underlying map because of lazy (generation-based) deletion. |
42 | | enum InsertLoc<'a, K: 'a, V: 'a> { |
43 | | Vacant(crate::ctxhash::VacantEntry<'a, K, Val<V>>), |
44 | | Occupied(crate::ctxhash::OccupiedEntry<'a, K, Val<V>>), |
45 | | } |
46 | | |
47 | | impl<'a, K, V> VacantEntry<'a, K, V> { |
48 | | /// Sets the value of the entry with the `VacantEntry`'s key. |
49 | 30.6M | pub fn insert(self, value: V) { |
50 | 30.6M | let val = Val { |
51 | 30.6M | value, |
52 | 30.6M | level: self.depth, |
53 | 30.6M | generation: self.generation, |
54 | 30.6M | }; |
55 | 30.6M | match self.entry { |
56 | 28.4M | InsertLoc::Vacant(v) => { |
57 | 28.4M | v.insert(val); |
58 | 28.4M | } |
59 | 2.19M | InsertLoc::Occupied(mut o) => { |
60 | 2.19M | *o.get_mut() = val; |
61 | 2.19M | } |
62 | | } |
63 | 30.6M | } <cranelift_codegen::scoped_hash_map::VacantEntry<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::insert Line | Count | Source | 49 | 30.0M | pub fn insert(self, value: V) { | 50 | 30.0M | let val = Val { | 51 | 30.0M | value, | 52 | 30.0M | level: self.depth, | 53 | 30.0M | generation: self.generation, | 54 | 30.0M | }; | 55 | 30.0M | match self.entry { | 56 | 27.8M | InsertLoc::Vacant(v) => { | 57 | 27.8M | v.insert(val); | 58 | 27.8M | } | 59 | 2.19M | InsertLoc::Occupied(mut o) => { | 60 | 2.19M | *o.get_mut() = val; | 61 | 2.19M | } | 62 | | } | 63 | 30.0M | } |
<cranelift_codegen::scoped_hash_map::VacantEntry<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::insert Line | Count | Source | 49 | 639k | pub fn insert(self, value: V) { | 50 | 639k | let val = Val { | 51 | 639k | value, | 52 | 639k | level: self.depth, | 53 | 639k | generation: self.generation, | 54 | 639k | }; | 55 | 639k | match self.entry { | 56 | 636k | InsertLoc::Vacant(v) => { | 57 | 636k | v.insert(val); | 58 | 636k | } | 59 | 3.35k | InsertLoc::Occupied(mut o) => { | 60 | 3.35k | *o.get_mut() = val; | 61 | 3.35k | } | 62 | | } | 63 | 639k | } |
|
64 | | } |
65 | | |
66 | | /// A view into a single entry in a map, which may either be vacant or occupied. |
67 | | /// |
68 | | /// This enum is constructed from the `entry` method on `ScopedHashMap`. |
69 | | pub enum Entry<'a, K: 'a, V: 'a> { |
70 | | Occupied(OccupiedEntry<'a, K, V>), |
71 | | Vacant(VacantEntry<'a, K, V>), |
72 | | } |
73 | | |
74 | | /// A wrapper around a `FxHashMap` which adds the concept of scopes. Items inserted |
75 | | /// within a scope are removed when the scope is exited. |
76 | | /// |
77 | | /// Shadowing, where one scope has entries with the same keys as a containing scope, |
78 | | /// is not supported in this implementation. |
79 | | pub struct ScopedHashMap<K, V> { |
80 | | map: CtxHashMap<K, Val<V>>, |
81 | | generation_by_depth: SmallVec<[u32; 8]>, |
82 | | generation: u32, |
83 | | } |
84 | | |
85 | | impl<K, V> ScopedHashMap<K, V> |
86 | | where |
87 | | K: Clone, |
88 | | { |
89 | | /// Creates an empty `ScopedHashMap`. |
90 | | #[cfg(test)] |
91 | | pub fn new() -> Self { |
92 | | Self::with_capacity(16) |
93 | | } |
94 | | |
95 | | /// Creates an empty `ScopedHashMap` with some pre-allocated capacity. |
96 | 2.29M | pub fn with_capacity(cap: usize) -> Self { |
97 | | Self { |
98 | 2.29M | map: CtxHashMap::with_capacity(cap), |
99 | | generation: 0, |
100 | 2.29M | generation_by_depth: smallvec![0], |
101 | | } |
102 | 2.29M | } <cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::with_capacity Line | Count | Source | 96 | 1.14M | pub fn with_capacity(cap: usize) -> Self { | 97 | | Self { | 98 | 1.14M | map: CtxHashMap::with_capacity(cap), | 99 | | generation: 0, | 100 | 1.14M | generation_by_depth: smallvec![0], | 101 | | } | 102 | 1.14M | } |
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::with_capacity Line | Count | Source | 96 | 1.14M | pub fn with_capacity(cap: usize) -> Self { | 97 | | Self { | 98 | 1.14M | map: CtxHashMap::with_capacity(cap), | 99 | | generation: 0, | 100 | 1.14M | generation_by_depth: smallvec![0], | 101 | | } | 102 | 1.14M | } |
|
103 | | |
104 | | /// Similar to `FxHashMap::entry`, gets the given key's corresponding entry in the map for |
105 | | /// in-place manipulation. |
106 | 695k | pub fn entry<'a, C>(&'a mut self, ctx: &C, key: K) -> Entry<'a, K, V> |
107 | 695k | where |
108 | 695k | C: CtxEq<K, K> + CtxHash<K>, |
109 | | { |
110 | 695k | self.entry_with_depth(ctx, key, self.depth()) |
111 | 695k | } |
112 | | |
113 | | /// Get the entry, setting the scope depth at which to insert. |
114 | 33.1M | pub fn entry_with_depth<'a, C>(&'a mut self, ctx: &C, key: K, depth: usize) -> Entry<'a, K, V> |
115 | 33.1M | where |
116 | 33.1M | C: CtxEq<K, K> + CtxHash<K>, |
117 | | { |
118 | 33.1M | debug_assert!(depth <= self.generation_by_depth.len()); |
119 | 33.1M | let generation = self.generation_by_depth[depth]; |
120 | 33.1M | let depth = depth as u32; |
121 | 33.1M | match self.map.entry(key, ctx) { |
122 | 4.70M | crate::ctxhash::Entry::Occupied(entry) => { |
123 | 4.70M | let entry_generation = entry.get().generation; |
124 | 4.70M | let entry_depth = entry.get().level as usize; |
125 | 4.70M | if self.generation_by_depth.get(entry_depth).cloned() == Some(entry_generation) { |
126 | 2.50M | Entry::Occupied(OccupiedEntry { entry }) |
127 | | } else { |
128 | 2.19M | Entry::Vacant(VacantEntry { |
129 | 2.19M | entry: InsertLoc::Occupied(entry), |
130 | 2.19M | depth, |
131 | 2.19M | generation, |
132 | 2.19M | }) |
133 | | } |
134 | | } |
135 | 28.4M | crate::ctxhash::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { |
136 | 28.4M | entry: InsertLoc::Vacant(entry), |
137 | 28.4M | depth, |
138 | 28.4M | generation, |
139 | 28.4M | }), |
140 | | } |
141 | 33.1M | } <cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::entry_with_depth::<cranelift_codegen::ctxhash::NullCtx> Line | Count | Source | 114 | 32.4M | pub fn entry_with_depth<'a, C>(&'a mut self, ctx: &C, key: K, depth: usize) -> Entry<'a, K, V> | 115 | 32.4M | where | 116 | 32.4M | C: CtxEq<K, K> + CtxHash<K>, | 117 | | { | 118 | 32.4M | debug_assert!(depth <= self.generation_by_depth.len()); | 119 | 32.4M | let generation = self.generation_by_depth[depth]; | 120 | 32.4M | let depth = depth as u32; | 121 | 32.4M | match self.map.entry(key, ctx) { | 122 | 4.64M | crate::ctxhash::Entry::Occupied(entry) => { | 123 | 4.64M | let entry_generation = entry.get().generation; | 124 | 4.64M | let entry_depth = entry.get().level as usize; | 125 | 4.64M | if self.generation_by_depth.get(entry_depth).cloned() == Some(entry_generation) { | 126 | 2.45M | Entry::Occupied(OccupiedEntry { entry }) | 127 | | } else { | 128 | 2.19M | Entry::Vacant(VacantEntry { | 129 | 2.19M | entry: InsertLoc::Occupied(entry), | 130 | 2.19M | depth, | 131 | 2.19M | generation, | 132 | 2.19M | }) | 133 | | } | 134 | | } | 135 | 27.8M | crate::ctxhash::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { | 136 | 27.8M | entry: InsertLoc::Vacant(entry), | 137 | 27.8M | depth, | 138 | 27.8M | generation, | 139 | 27.8M | }), | 140 | | } | 141 | 32.4M | } |
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::entry_with_depth::<cranelift_codegen::ctxhash::NullCtx> Line | Count | Source | 114 | 695k | pub fn entry_with_depth<'a, C>(&'a mut self, ctx: &C, key: K, depth: usize) -> Entry<'a, K, V> | 115 | 695k | where | 116 | 695k | C: CtxEq<K, K> + CtxHash<K>, | 117 | | { | 118 | 695k | debug_assert!(depth <= self.generation_by_depth.len()); | 119 | 695k | let generation = self.generation_by_depth[depth]; | 120 | 695k | let depth = depth as u32; | 121 | 695k | match self.map.entry(key, ctx) { | 122 | 59.6k | crate::ctxhash::Entry::Occupied(entry) => { | 123 | 59.6k | let entry_generation = entry.get().generation; | 124 | 59.6k | let entry_depth = entry.get().level as usize; | 125 | 59.6k | if self.generation_by_depth.get(entry_depth).cloned() == Some(entry_generation) { | 126 | 56.2k | Entry::Occupied(OccupiedEntry { entry }) | 127 | | } else { | 128 | 3.35k | Entry::Vacant(VacantEntry { | 129 | 3.35k | entry: InsertLoc::Occupied(entry), | 130 | 3.35k | depth, | 131 | 3.35k | generation, | 132 | 3.35k | }) | 133 | | } | 134 | | } | 135 | 636k | crate::ctxhash::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { | 136 | 636k | entry: InsertLoc::Vacant(entry), | 137 | 636k | depth, | 138 | 636k | generation, | 139 | 636k | }), | 140 | | } | 141 | 695k | } |
|
142 | | |
143 | | /// Get a value from a key, if present. |
144 | 133M | pub fn get<'a, C>(&'a self, ctx: &C, key: &K) -> Option<&'a V> |
145 | 133M | where |
146 | 133M | C: CtxEq<K, K> + CtxHash<K>, |
147 | | { |
148 | 133M | self.map |
149 | 133M | .get(key, ctx) |
150 | 133M | .filter(|entry| { |
151 | 58.7M | let level = entry.level as usize; |
152 | 58.7M | self.generation_by_depth.get(level).cloned() == Some(entry.generation) |
153 | 58.7M | }) <cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::get::<cranelift_codegen::ctxhash::NullCtx>::{closure#0}Line | Count | Source | 150 | 29.2M | .filter(|entry| { | 151 | 29.2M | let level = entry.level as usize; | 152 | 29.2M | self.generation_by_depth.get(level).cloned() == Some(entry.generation) | 153 | 29.2M | }) |
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::get::<cranelift_codegen::egraph::GVNContext>::{closure#0}Line | Count | Source | 150 | 29.4M | .filter(|entry| { | 151 | 29.4M | let level = entry.level as usize; | 152 | 29.4M | self.generation_by_depth.get(level).cloned() == Some(entry.generation) | 153 | 29.4M | }) |
|
154 | 133M | .map(|entry| &entry.value) |
155 | 133M | } <cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::get::<cranelift_codegen::ctxhash::NullCtx> Line | Count | Source | 144 | 66.1M | pub fn get<'a, C>(&'a self, ctx: &C, key: &K) -> Option<&'a V> | 145 | 66.1M | where | 146 | 66.1M | C: CtxEq<K, K> + CtxHash<K>, | 147 | | { | 148 | 66.1M | self.map | 149 | 66.1M | .get(key, ctx) | 150 | 66.1M | .filter(|entry| { | 151 | | let level = entry.level as usize; | 152 | | self.generation_by_depth.get(level).cloned() == Some(entry.generation) | 153 | | }) | 154 | 66.1M | .map(|entry| &entry.value) | 155 | 66.1M | } |
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::get::<cranelift_codegen::egraph::GVNContext> Line | Count | Source | 144 | 67.0M | pub fn get<'a, C>(&'a self, ctx: &C, key: &K) -> Option<&'a V> | 145 | 67.0M | where | 146 | 67.0M | C: CtxEq<K, K> + CtxHash<K>, | 147 | | { | 148 | 67.0M | self.map | 149 | 67.0M | .get(key, ctx) | 150 | 67.0M | .filter(|entry| { | 151 | | let level = entry.level as usize; | 152 | | self.generation_by_depth.get(level).cloned() == Some(entry.generation) | 153 | | }) | 154 | 67.0M | .map(|entry| &entry.value) | 155 | 67.0M | } |
|
156 | | |
157 | | /// Insert a key-value pair if absent. No-op if already exists. |
158 | 11.4M | pub fn insert_if_absent<C>(&mut self, ctx: &C, key: K, value: V) |
159 | 11.4M | where |
160 | 11.4M | C: CtxEq<K, K> + CtxHash<K>, |
161 | | { |
162 | 11.4M | self.insert_if_absent_with_depth(ctx, key, value, self.depth()); |
163 | 11.4M | } |
164 | | |
165 | | /// Insert a key-value pair if absent, using the given depth for |
166 | | /// the insertion. No-op if already exists. |
167 | 32.4M | pub fn insert_if_absent_with_depth<C>(&mut self, ctx: &C, key: K, value: V, depth: usize) |
168 | 32.4M | where |
169 | 32.4M | C: CtxEq<K, K> + CtxHash<K>, |
170 | | { |
171 | 32.4M | match self.entry_with_depth(ctx, key, depth) { |
172 | 30.0M | Entry::Vacant(v) => { |
173 | 30.0M | v.insert(value); |
174 | 30.0M | } |
175 | 2.45M | Entry::Occupied(_) => { |
176 | 2.45M | // Nothing. |
177 | 2.45M | } |
178 | | } |
179 | 32.4M | } |
180 | | |
181 | | /// Insert a key-value pair, using the given depth for the |
182 | | /// insertion. Removes existing entry and overwrites if already |
183 | | /// existed. |
184 | 37.5M | pub fn insert_with_depth<C>(&mut self, ctx: &C, key: K, value: V, depth: usize) |
185 | 37.5M | where |
186 | 37.5M | C: CtxEq<K, K> + CtxHash<K>, |
187 | | { |
188 | 37.5M | let val = Val { |
189 | 37.5M | value, |
190 | 37.5M | level: depth as u32, |
191 | 37.5M | generation: self.generation_by_depth[depth], |
192 | 37.5M | }; |
193 | 37.5M | self.map.insert(key, val, ctx); |
194 | 37.5M | } |
195 | | |
196 | | /// Enter a new scope. |
197 | 20.1M | pub fn increment_depth(&mut self) { |
198 | 20.1M | self.generation_by_depth.push(self.generation); |
199 | 20.1M | } <cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::increment_depth Line | Count | Source | 197 | 10.0M | pub fn increment_depth(&mut self) { | 198 | 10.0M | self.generation_by_depth.push(self.generation); | 199 | 10.0M | } |
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::increment_depth Line | Count | Source | 197 | 10.0M | pub fn increment_depth(&mut self) { | 198 | 10.0M | self.generation_by_depth.push(self.generation); | 199 | 10.0M | } |
|
200 | | |
201 | | /// Exit the current scope. |
202 | 16.6M | pub fn decrement_depth(&mut self) { |
203 | 16.6M | self.generation += 1; |
204 | 16.6M | self.generation_by_depth.pop(); |
205 | 16.6M | } <cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::decrement_depth Line | Count | Source | 202 | 10.0M | pub fn decrement_depth(&mut self) { | 203 | 10.0M | self.generation += 1; | 204 | 10.0M | self.generation_by_depth.pop(); | 205 | 10.0M | } |
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::decrement_depth Line | Count | Source | 202 | 6.58M | pub fn decrement_depth(&mut self) { | 203 | 6.58M | self.generation += 1; | 204 | 6.58M | self.generation_by_depth.pop(); | 205 | 6.58M | } |
|
206 | | |
207 | | /// Return the current scope depth. |
208 | 32.8M | pub fn depth(&self) -> usize { |
209 | 32.8M | self.generation_by_depth |
210 | 32.8M | .len() |
211 | 32.8M | .checked_sub(1) |
212 | 32.8M | .expect("generation_by_depth cannot be empty") |
213 | 32.8M | } <cranelift_codegen::scoped_hash_map::ScopedHashMap<cranelift_codegen::ir::entities::Value, cranelift_codegen::egraph::elaborate::ElaboratedValue>>::depth Line | Count | Source | 208 | 32.1M | pub fn depth(&self) -> usize { | 209 | 32.1M | self.generation_by_depth | 210 | 32.1M | .len() | 211 | 32.1M | .checked_sub(1) | 212 | 32.1M | .expect("generation_by_depth cannot be empty") | 213 | 32.1M | } |
<cranelift_codegen::scoped_hash_map::ScopedHashMap<(cranelift_codegen::ir::types::Type, cranelift_codegen::ir::instructions::InstructionData), core::option::Option<cranelift_codegen::ir::entities::Value>>>::depth Line | Count | Source | 208 | 695k | pub fn depth(&self) -> usize { | 209 | 695k | self.generation_by_depth | 210 | 695k | .len() | 211 | 695k | .checked_sub(1) | 212 | 695k | .expect("generation_by_depth cannot be empty") | 213 | 695k | } |
|
214 | | } |
215 | | |
216 | | #[cfg(test)] |
217 | | mod tests { |
218 | | use super::*; |
219 | | use crate::ctxhash::NullCtx; |
220 | | |
221 | | #[test] |
222 | | fn basic() { |
223 | | let mut map: ScopedHashMap<i32, i32> = ScopedHashMap::new(); |
224 | | |
225 | | match map.entry(&NullCtx, 0) { |
226 | | Entry::Occupied(_entry) => panic!(), |
227 | | Entry::Vacant(entry) => entry.insert(1), |
228 | | } |
229 | | match map.entry(&NullCtx, 2) { |
230 | | Entry::Occupied(_entry) => panic!(), |
231 | | Entry::Vacant(entry) => entry.insert(8), |
232 | | } |
233 | | match map.entry(&NullCtx, 2) { |
234 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
235 | | Entry::Vacant(_entry) => panic!(), |
236 | | } |
237 | | map.increment_depth(); |
238 | | match map.entry(&NullCtx, 2) { |
239 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
240 | | Entry::Vacant(_entry) => panic!(), |
241 | | } |
242 | | match map.entry(&NullCtx, 1) { |
243 | | Entry::Occupied(_entry) => panic!(), |
244 | | Entry::Vacant(entry) => entry.insert(3), |
245 | | } |
246 | | match map.entry(&NullCtx, 1) { |
247 | | Entry::Occupied(entry) => assert!(*entry.get() == 3), |
248 | | Entry::Vacant(_entry) => panic!(), |
249 | | } |
250 | | match map.entry(&NullCtx, 0) { |
251 | | Entry::Occupied(entry) => assert!(*entry.get() == 1), |
252 | | Entry::Vacant(_entry) => panic!(), |
253 | | } |
254 | | match map.entry(&NullCtx, 2) { |
255 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
256 | | Entry::Vacant(_entry) => panic!(), |
257 | | } |
258 | | map.decrement_depth(); |
259 | | match map.entry(&NullCtx, 0) { |
260 | | Entry::Occupied(entry) => assert!(*entry.get() == 1), |
261 | | Entry::Vacant(_entry) => panic!(), |
262 | | } |
263 | | match map.entry(&NullCtx, 2) { |
264 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
265 | | Entry::Vacant(_entry) => panic!(), |
266 | | } |
267 | | map.increment_depth(); |
268 | | match map.entry(&NullCtx, 2) { |
269 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
270 | | Entry::Vacant(_entry) => panic!(), |
271 | | } |
272 | | match map.entry(&NullCtx, 1) { |
273 | | Entry::Occupied(_entry) => panic!(), |
274 | | Entry::Vacant(entry) => entry.insert(4), |
275 | | } |
276 | | match map.entry(&NullCtx, 1) { |
277 | | Entry::Occupied(entry) => assert!(*entry.get() == 4), |
278 | | Entry::Vacant(_entry) => panic!(), |
279 | | } |
280 | | match map.entry(&NullCtx, 2) { |
281 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
282 | | Entry::Vacant(_entry) => panic!(), |
283 | | } |
284 | | map.decrement_depth(); |
285 | | map.increment_depth(); |
286 | | map.increment_depth(); |
287 | | map.increment_depth(); |
288 | | match map.entry(&NullCtx, 2) { |
289 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
290 | | Entry::Vacant(_entry) => panic!(), |
291 | | } |
292 | | match map.entry(&NullCtx, 1) { |
293 | | Entry::Occupied(_entry) => panic!(), |
294 | | Entry::Vacant(entry) => entry.insert(5), |
295 | | } |
296 | | match map.entry(&NullCtx, 1) { |
297 | | Entry::Occupied(entry) => assert!(*entry.get() == 5), |
298 | | Entry::Vacant(_entry) => panic!(), |
299 | | } |
300 | | match map.entry(&NullCtx, 2) { |
301 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
302 | | Entry::Vacant(_entry) => panic!(), |
303 | | } |
304 | | map.decrement_depth(); |
305 | | map.decrement_depth(); |
306 | | map.decrement_depth(); |
307 | | match map.entry(&NullCtx, 2) { |
308 | | Entry::Occupied(entry) => assert!(*entry.get() == 8), |
309 | | Entry::Vacant(_entry) => panic!(), |
310 | | } |
311 | | match map.entry(&NullCtx, 1) { |
312 | | Entry::Occupied(_entry) => panic!(), |
313 | | Entry::Vacant(entry) => entry.insert(3), |
314 | | } |
315 | | } |
316 | | |
317 | | #[test] |
318 | | fn insert_arbitrary_depth() { |
319 | | let mut map: ScopedHashMap<i32, i32> = ScopedHashMap::new(); |
320 | | map.insert_if_absent(&NullCtx, 1, 2); |
321 | | assert_eq!(map.get(&NullCtx, &1), Some(&2)); |
322 | | map.increment_depth(); |
323 | | assert_eq!(map.get(&NullCtx, &1), Some(&2)); |
324 | | map.insert_if_absent(&NullCtx, 3, 4); |
325 | | assert_eq!(map.get(&NullCtx, &3), Some(&4)); |
326 | | map.decrement_depth(); |
327 | | assert_eq!(map.get(&NullCtx, &3), None); |
328 | | map.increment_depth(); |
329 | | map.insert_if_absent_with_depth(&NullCtx, 3, 4, 0); |
330 | | assert_eq!(map.get(&NullCtx, &3), Some(&4)); |
331 | | map.decrement_depth(); |
332 | | assert_eq!(map.get(&NullCtx, &3), Some(&4)); |
333 | | } |
334 | | } |