├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── measurements.txt ├── src ├── bitvec.rs ├── lib.rs ├── snapshot_vec.rs ├── undo_log.rs └── unify │ ├── backing_vec.rs │ ├── mod.rs │ └── tests.rs └── tests └── external_undo_log.rs /.gitignore: -------------------------------------------------------------------------------- 1 | TAGS 2 | /target 3 | /Cargo.lock 4 | *~ 5 | *.rs.bk 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - nightly 5 | script: 6 | - | 7 | if [[ $TRAVIS_RUST_VERSION == *stable* ]] 8 | then 9 | rustup component add rustfmt 10 | cargo fmt --version 11 | cargo fmt -- --check || (echo "Please reformat your code with 'cargo fmt' (version $(cargo fmt --version))"; false) 12 | fi 13 | - cargo test 14 | - | 15 | [ $TRAVIS_RUST_VERSION != nightly ] || 16 | cargo test --all-features 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ena" 3 | description = "Union-find, congruence closure, and other unification code. Based on code from rustc." 4 | license = "MIT OR Apache-2.0" 5 | homepage = "https://github.com/rust-lang/ena" 6 | repository = "https://github.com/rust-lang/ena" 7 | version = "0.14.3" 8 | authors = ["Niko Matsakis "] 9 | readme = "README.md" 10 | keywords = ["unification", "union-find"] 11 | 12 | [features] 13 | bench = [ ] 14 | persistent = [ "dogged" ] 15 | 16 | [dependencies] 17 | dogged = { version = "0.2.0", optional = true } 18 | log = "0.4" 19 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/rust-lang/ena.svg?branch=master)](https://travis-ci.org/rust-lang/ena) 2 | 3 | An implementation of union-find in Rust; extracted from (and used by) 4 | rustc. 5 | 6 | ### Name 7 | 8 | The name "ena" comes from the Greek word for "one". 9 | 10 | ### Features 11 | 12 | By default, you just get the union-find implementation. You can also 13 | opt-in to the following experimental features: 14 | 15 | - `bench`: use to run benchmarks (`cargo bench --features bench`) 16 | 17 | ### License 18 | 19 | Like rustc itself, this code is dual-licensed under the MIT and Apache 20 | licenses. Pull requests, comments, and other contributions are assumed 21 | to imply consent to those terms. Moreover, it is understood that any 22 | changes here may well be used in rustc itself under the same terms. 23 | -------------------------------------------------------------------------------- /measurements.txt: -------------------------------------------------------------------------------- 1 | base 2 | test unify::tests::big_array_bench ... bench: 740,192 ns/iter (+/- 35,823) 3 | test unify::tests::big_array_bench ... bench: 745,031 ns/iter (+/- 240,463) 4 | test unify::tests::big_array_bench ... bench: 762,031 ns/iter (+/- 240,463) 5 | test unify::tests::big_array_bench ... bench: 756,234 ns/iter (+/- 264,710) 6 | 7 | -------------------------------------------------------------------------------- /src/bitvec.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | /// A very simple BitVector type. 12 | pub struct BitVector { 13 | data: Vec, 14 | } 15 | 16 | impl BitVector { 17 | pub fn new(num_bits: usize) -> BitVector { 18 | let num_words = u64s(num_bits); 19 | BitVector { data: vec![0; num_words] } 20 | } 21 | 22 | pub fn contains(&self, bit: usize) -> bool { 23 | let (word, mask) = word_mask(bit); 24 | (self.data[word] & mask) != 0 25 | } 26 | 27 | /// Returns true if the bit has changed. 28 | pub fn insert(&mut self, bit: usize) -> bool { 29 | let (word, mask) = word_mask(bit); 30 | let data = &mut self.data[word]; 31 | let value = *data; 32 | let new_value = value | mask; 33 | *data = new_value; 34 | new_value != value 35 | } 36 | 37 | pub fn insert_all(&mut self, all: &BitVector) -> bool { 38 | assert!(self.data.len() == all.data.len()); 39 | let mut changed = false; 40 | for (i, j) in self.data.iter_mut().zip(&all.data) { 41 | let value = *i; 42 | *i = value | *j; 43 | if value != *i { 44 | changed = true; 45 | } 46 | } 47 | changed 48 | } 49 | 50 | pub fn grow(&mut self, num_bits: usize) { 51 | let num_words = u64s(num_bits); 52 | let extra_words = self.data.len() - num_words; 53 | self.data.extend((0..extra_words).map(|_| 0)); 54 | } 55 | 56 | /// Iterates over indexes of set bits in a sorted order 57 | pub fn iter<'a>(&'a self) -> BitVectorIter<'a> { 58 | BitVectorIter { 59 | iter: self.data.iter(), 60 | current: 0, 61 | idx: 0, 62 | } 63 | } 64 | } 65 | 66 | pub struct BitVectorIter<'a> { 67 | iter: ::std::slice::Iter<'a, u64>, 68 | current: u64, 69 | idx: usize, 70 | } 71 | 72 | impl<'a> Iterator for BitVectorIter<'a> { 73 | type Item = usize; 74 | fn next(&mut self) -> Option { 75 | while self.current == 0 { 76 | self.current = if let Some(&i) = self.iter.next() { 77 | if i == 0 { 78 | self.idx += 64; 79 | continue; 80 | } else { 81 | self.idx = u64s(self.idx) * 64; 82 | i 83 | } 84 | } else { 85 | return None; 86 | } 87 | } 88 | let offset = self.current.trailing_zeros() as usize; 89 | self.current >>= offset; 90 | self.current >>= 1; // shift otherwise overflows for 0b1000_0000_…_0000 91 | self.idx += offset + 1; 92 | return Some(self.idx - 1); 93 | } 94 | } 95 | 96 | /// A "bit matrix" is basically a square matrix of booleans 97 | /// represented as one gigantic bitvector. In other words, it is as if 98 | /// you have N bitvectors, each of length N. Note that `elements` here is `N`/ 99 | #[derive(Clone)] 100 | pub struct BitMatrix { 101 | elements: usize, 102 | vector: Vec, 103 | } 104 | 105 | impl BitMatrix { 106 | // Create a new `elements x elements` matrix, initially empty. 107 | pub fn new(elements: usize) -> BitMatrix { 108 | // For every element, we need one bit for every other 109 | // element. Round up to an even number of u64s. 110 | let u64s_per_elem = u64s(elements); 111 | BitMatrix { 112 | elements: elements, 113 | vector: vec![0; elements * u64s_per_elem], 114 | } 115 | } 116 | 117 | /// The range of bits for a given element. 118 | fn range(&self, element: usize) -> (usize, usize) { 119 | let u64s_per_elem = u64s(self.elements); 120 | let start = element * u64s_per_elem; 121 | (start, start + u64s_per_elem) 122 | } 123 | 124 | pub fn add(&mut self, source: usize, target: usize) -> bool { 125 | let (start, _) = self.range(source); 126 | let (word, mask) = word_mask(target); 127 | let mut vector = &mut self.vector[..]; 128 | let v1 = vector[start + word]; 129 | let v2 = v1 | mask; 130 | vector[start + word] = v2; 131 | v1 != v2 132 | } 133 | 134 | /// Do the bits from `source` contain `target`? 135 | /// 136 | /// Put another way, if the matrix represents (transitive) 137 | /// reachability, can `source` reach `target`? 138 | pub fn contains(&self, source: usize, target: usize) -> bool { 139 | let (start, _) = self.range(source); 140 | let (word, mask) = word_mask(target); 141 | (self.vector[start + word] & mask) != 0 142 | } 143 | 144 | /// Returns those indices that are reachable from both `a` and 145 | /// `b`. This is an O(n) operation where `n` is the number of 146 | /// elements (somewhat independent from the actual size of the 147 | /// intersection, in particular). 148 | pub fn intersection(&self, a: usize, b: usize) -> Vec { 149 | let (a_start, a_end) = self.range(a); 150 | let (b_start, b_end) = self.range(b); 151 | let mut result = Vec::with_capacity(self.elements); 152 | for (base, (i, j)) in (a_start..a_end).zip(b_start..b_end).enumerate() { 153 | let mut v = self.vector[i] & self.vector[j]; 154 | for bit in 0..64 { 155 | if v == 0 { 156 | break; 157 | } 158 | if v & 0x1 != 0 { 159 | result.push(base * 64 + bit); 160 | } 161 | v >>= 1; 162 | } 163 | } 164 | result 165 | } 166 | 167 | /// Add the bits from `read` to the bits from `write`, 168 | /// return true if anything changed. 169 | /// 170 | /// This is used when computing transitive reachability because if 171 | /// you have an edge `write -> read`, because in that case 172 | /// `write` can reach everything that `read` can (and 173 | /// potentially more). 174 | pub fn merge(&mut self, read: usize, write: usize) -> bool { 175 | let (read_start, read_end) = self.range(read); 176 | let (write_start, write_end) = self.range(write); 177 | let vector = &mut self.vector[..]; 178 | let mut changed = false; 179 | for (read_index, write_index) in (read_start..read_end).zip(write_start..write_end) { 180 | let v1 = vector[write_index]; 181 | let v2 = v1 | vector[read_index]; 182 | vector[write_index] = v2; 183 | changed = changed | (v1 != v2); 184 | } 185 | changed 186 | } 187 | } 188 | 189 | fn u64s(elements: usize) -> usize { 190 | (elements + 63) / 64 191 | } 192 | 193 | fn word_mask(index: usize) -> (usize, u64) { 194 | let word = index / 64; 195 | let mask = 1 << (index % 64); 196 | (word, mask) 197 | } 198 | 199 | #[test] 200 | fn bitvec_iter_works() { 201 | let mut bitvec = BitVector::new(100); 202 | bitvec.insert(1); 203 | bitvec.insert(10); 204 | bitvec.insert(19); 205 | bitvec.insert(62); 206 | bitvec.insert(63); 207 | bitvec.insert(64); 208 | bitvec.insert(65); 209 | bitvec.insert(66); 210 | bitvec.insert(99); 211 | assert_eq!(bitvec.iter().collect::>(), 212 | [1, 10, 19, 62, 63, 64, 65, 66, 99]); 213 | } 214 | 215 | #[test] 216 | fn bitvec_iter_works_2() { 217 | let mut bitvec = BitVector::new(300); 218 | bitvec.insert(1); 219 | bitvec.insert(10); 220 | bitvec.insert(19); 221 | bitvec.insert(62); 222 | bitvec.insert(66); 223 | bitvec.insert(99); 224 | bitvec.insert(299); 225 | assert_eq!(bitvec.iter().collect::>(), 226 | [1, 10, 19, 62, 66, 99, 299]); 227 | 228 | } 229 | 230 | #[test] 231 | fn bitvec_iter_works_3() { 232 | let mut bitvec = BitVector::new(319); 233 | bitvec.insert(0); 234 | bitvec.insert(127); 235 | bitvec.insert(191); 236 | bitvec.insert(255); 237 | bitvec.insert(319); 238 | assert_eq!(bitvec.iter().collect::>(), [0, 127, 191, 255, 319]); 239 | } 240 | 241 | #[test] 242 | fn union_two_vecs() { 243 | let mut vec1 = BitVector::new(65); 244 | let mut vec2 = BitVector::new(65); 245 | assert!(vec1.insert(3)); 246 | assert!(!vec1.insert(3)); 247 | assert!(vec2.insert(5)); 248 | assert!(vec2.insert(64)); 249 | assert!(vec1.insert_all(&vec2)); 250 | assert!(!vec1.insert_all(&vec2)); 251 | assert!(vec1.contains(3)); 252 | assert!(!vec1.contains(4)); 253 | assert!(vec1.contains(5)); 254 | assert!(!vec1.contains(63)); 255 | assert!(vec1.contains(64)); 256 | } 257 | 258 | #[test] 259 | fn grow() { 260 | let mut vec1 = BitVector::new(65); 261 | assert!(vec1.insert(3)); 262 | assert!(!vec1.insert(3)); 263 | assert!(vec1.insert(5)); 264 | assert!(vec1.insert(64)); 265 | vec1.grow(128); 266 | assert!(vec1.contains(3)); 267 | assert!(vec1.contains(5)); 268 | assert!(vec1.contains(64)); 269 | assert!(!vec1.contains(126)); 270 | } 271 | 272 | #[test] 273 | fn matrix_intersection() { 274 | let mut vec1 = BitMatrix::new(200); 275 | 276 | // (*) Elements reachable from both 2 and 65. 277 | 278 | vec1.add(2, 3); 279 | vec1.add(2, 6); 280 | vec1.add(2, 10); // (*) 281 | vec1.add(2, 64); // (*) 282 | vec1.add(2, 65); 283 | vec1.add(2, 130); 284 | vec1.add(2, 160); // (*) 285 | 286 | vec1.add(64, 133); 287 | 288 | vec1.add(65, 2); 289 | vec1.add(65, 8); 290 | vec1.add(65, 10); // (*) 291 | vec1.add(65, 64); // (*) 292 | vec1.add(65, 68); 293 | vec1.add(65, 133); 294 | vec1.add(65, 160); // (*) 295 | 296 | let intersection = vec1.intersection(2, 64); 297 | assert!(intersection.is_empty()); 298 | 299 | let intersection = vec1.intersection(2, 65); 300 | assert_eq!(intersection, &[10, 64, 160]); 301 | } 302 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! An implementation of union-find. See the `unify` module for more 12 | //! details. 13 | 14 | #![cfg_attr(feature = "bench", feature(test))] 15 | 16 | #[macro_use] 17 | extern crate log; 18 | 19 | #[cfg(feature = "persistent")] 20 | extern crate dogged; 21 | 22 | pub mod snapshot_vec; 23 | pub mod undo_log; 24 | pub mod unify; 25 | -------------------------------------------------------------------------------- /src/snapshot_vec.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! A utility class for implementing "snapshottable" things; a snapshottable data structure permits 12 | //! you to take a snapshot (via `start_snapshot`) and then, after making some changes, elect either 13 | //! to rollback to the start of the snapshot or commit those changes. 14 | //! 15 | //! This vector is intended to be used as part of an abstraction, not serve as a complete 16 | //! abstraction on its own. As such, while it will roll back most changes on its own, it also 17 | //! supports a `get_mut` operation that gives you an arbitrary mutable pointer into the vector. To 18 | //! ensure that any changes you make this with this pointer are rolled back, you must invoke 19 | //! `record` to record any changes you make and also supplying a delegate capable of reversing 20 | //! those changes. 21 | 22 | use self::UndoLog::*; 23 | 24 | use std::fmt; 25 | use std::marker::PhantomData; 26 | use std::mem; 27 | use std::ops; 28 | 29 | use undo_log::{Rollback, Snapshots, UndoLogs, VecLog}; 30 | 31 | #[derive(Debug)] 32 | pub enum UndoLog { 33 | /// New variable with given index was created. 34 | NewElem(usize), 35 | 36 | /// Variable with given index was changed *from* the given value. 37 | SetElem(usize, D::Value), 38 | 39 | /// Extensible set of actions 40 | Other(D::Undo), 41 | } 42 | 43 | impl Rollback> for SnapshotVecStorage { 44 | fn reverse(&mut self, undo: UndoLog) { 45 | self.values.reverse(undo) 46 | } 47 | } 48 | impl Rollback> for Vec { 49 | fn reverse(&mut self, undo: UndoLog) { 50 | match undo { 51 | NewElem(i) => { 52 | self.pop(); 53 | assert!(Vec::len(self) == i); 54 | } 55 | 56 | SetElem(i, v) => { 57 | self[i] = v; 58 | } 59 | 60 | Other(u) => { 61 | D::reverse(self, u); 62 | } 63 | } 64 | } 65 | } 66 | 67 | pub trait VecLike: AsRef<[D::Value]> + AsMut<[D::Value]> + Rollback> 68 | where 69 | D: SnapshotVecDelegate, 70 | { 71 | fn push(&mut self, item: D::Value); 72 | fn len(&self) -> usize; 73 | fn reserve(&mut self, size: usize); 74 | } 75 | 76 | impl VecLike for Vec 77 | where 78 | D: SnapshotVecDelegate, 79 | { 80 | fn push(&mut self, item: D::Value) { 81 | Vec::push(self, item) 82 | } 83 | fn len(&self) -> usize { 84 | Vec::len(self) 85 | } 86 | fn reserve(&mut self, size: usize) { 87 | Vec::reserve(self, size) 88 | } 89 | } 90 | 91 | impl VecLike for &'_ mut Vec 92 | where 93 | D: SnapshotVecDelegate, 94 | { 95 | fn push(&mut self, item: D::Value) { 96 | Vec::push(self, item) 97 | } 98 | fn len(&self) -> usize { 99 | Vec::len(self) 100 | } 101 | fn reserve(&mut self, size: usize) { 102 | Vec::reserve(self, size) 103 | } 104 | } 105 | 106 | #[allow(type_alias_bounds)] 107 | pub type SnapshotVecStorage = 108 | SnapshotVec::Value>, ()>; 109 | 110 | pub struct SnapshotVec< 111 | D: SnapshotVecDelegate, 112 | V: VecLike = Vec<::Value>, 113 | L = VecLog>, 114 | > { 115 | values: V, 116 | undo_log: L, 117 | _marker: PhantomData, 118 | } 119 | 120 | impl fmt::Debug for SnapshotVec 121 | where 122 | D: SnapshotVecDelegate, 123 | V: VecLike + fmt::Debug, 124 | L: fmt::Debug, 125 | { 126 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 127 | fmt.debug_struct("SnapshotVec") 128 | .field("values", &self.values) 129 | .field("undo_log", &self.undo_log) 130 | .finish() 131 | } 132 | } 133 | 134 | // Snapshots are tokens that should be created/consumed linearly. 135 | pub struct Snapshot { 136 | pub(crate) value_count: usize, 137 | snapshot: S, 138 | } 139 | 140 | pub trait SnapshotVecDelegate { 141 | type Value; 142 | type Undo; 143 | 144 | fn reverse(values: &mut Vec, action: Self::Undo); 145 | } 146 | 147 | // HACK(eddyb) manual impl avoids `Default` bound on `D`. 148 | impl + Default, L: Default> Default for SnapshotVec { 149 | fn default() -> Self { 150 | SnapshotVec { 151 | values: V::default(), 152 | undo_log: Default::default(), 153 | _marker: PhantomData, 154 | } 155 | } 156 | } 157 | 158 | impl + Default, L: Default> SnapshotVec { 159 | /// Creates a new `SnapshotVec`. If `L` is set to `()` then most mutating functions will not 160 | /// be accessible without calling `with_log` and supplying a compatibly `UndoLogs` instance. 161 | pub fn new() -> Self { 162 | Self::default() 163 | } 164 | } 165 | 166 | impl SnapshotVecStorage { 167 | /// Creates a `SnapshotVec` using the `undo_log`, allowing mutating methods to be called 168 | pub fn with_log<'a, L>( 169 | &'a mut self, 170 | undo_log: L, 171 | ) -> SnapshotVec::Value>, L> 172 | where 173 | L: UndoLogs>, 174 | { 175 | SnapshotVec { 176 | values: &mut self.values, 177 | undo_log, 178 | _marker: PhantomData, 179 | } 180 | } 181 | } 182 | 183 | impl SnapshotVec, L> { 184 | pub fn with_capacity(c: usize) -> Self { 185 | SnapshotVec { 186 | values: Vec::with_capacity(c), 187 | undo_log: Default::default(), 188 | _marker: PhantomData, 189 | } 190 | } 191 | } 192 | 193 | impl, D: SnapshotVecDelegate, U> SnapshotVec { 194 | pub fn len(&self) -> usize { 195 | self.values.len() 196 | } 197 | 198 | pub fn get(&self, index: usize) -> &D::Value { 199 | &self.values.as_ref()[index] 200 | } 201 | 202 | /// Returns a mutable pointer into the vec; whatever changes you make here cannot be undone 203 | /// automatically, so you should be sure call `record()` with some sort of suitable undo 204 | /// action. 205 | pub fn get_mut(&mut self, index: usize) -> &mut D::Value { 206 | &mut self.values.as_mut()[index] 207 | } 208 | 209 | /// Reserve space for new values, just like an ordinary vec. 210 | pub fn reserve(&mut self, additional: usize) { 211 | // This is not affected by snapshots or anything. 212 | self.values.reserve(additional); 213 | } 214 | } 215 | 216 | impl, D: SnapshotVecDelegate, L: UndoLogs>> SnapshotVec { 217 | fn in_snapshot(&self) -> bool { 218 | self.undo_log.in_snapshot() 219 | } 220 | 221 | pub fn record(&mut self, action: D::Undo) { 222 | if self.in_snapshot() { 223 | self.undo_log.push(Other(action)); 224 | } 225 | } 226 | 227 | pub fn push(&mut self, elem: D::Value) -> usize { 228 | let len = self.values.len(); 229 | self.values.push(elem); 230 | 231 | if self.in_snapshot() { 232 | self.undo_log.push(NewElem(len)); 233 | } 234 | 235 | len 236 | } 237 | 238 | /// Updates the element at the given index. The old value will saved (and perhaps restored) if 239 | /// a snapshot is active. 240 | pub fn set(&mut self, index: usize, new_elem: D::Value) { 241 | let old_elem = mem::replace(&mut self.values.as_mut()[index], new_elem); 242 | if self.undo_log.in_snapshot() { 243 | self.undo_log.push(SetElem(index, old_elem)); 244 | } 245 | } 246 | 247 | /// Updates all elements. Potentially more efficient -- but 248 | /// otherwise equivalent to -- invoking `set` for each element. 249 | pub fn set_all(&mut self, mut new_elems: impl FnMut(usize) -> D::Value) { 250 | if !self.undo_log.in_snapshot() { 251 | for (index, slot) in self.values.as_mut().iter_mut().enumerate() { 252 | *slot = new_elems(index); 253 | } 254 | } else { 255 | for i in 0..self.values.len() { 256 | self.set(i, new_elems(i)); 257 | } 258 | } 259 | } 260 | 261 | pub fn update(&mut self, index: usize, op: OP) 262 | where 263 | OP: FnOnce(&mut D::Value), 264 | D::Value: Clone, 265 | { 266 | if self.undo_log.in_snapshot() { 267 | let old_elem = self.values.as_mut()[index].clone(); 268 | self.undo_log.push(SetElem(index, old_elem)); 269 | } 270 | op(&mut self.values.as_mut()[index]); 271 | } 272 | } 273 | 274 | impl SnapshotVec 275 | where 276 | D: SnapshotVecDelegate, 277 | V: VecLike + Rollback>, 278 | L: Snapshots>, 279 | { 280 | pub fn start_snapshot(&mut self) -> Snapshot { 281 | Snapshot { 282 | value_count: self.values.len(), 283 | snapshot: self.undo_log.start_snapshot(), 284 | } 285 | } 286 | 287 | pub fn actions_since_snapshot(&self, snapshot: &Snapshot) -> &[UndoLog] { 288 | self.undo_log.actions_since_snapshot(&snapshot.snapshot) 289 | } 290 | 291 | pub fn rollback_to(&mut self, snapshot: Snapshot) { 292 | let values = &mut self.values; 293 | self.undo_log.rollback_to(|| values, snapshot.snapshot); 294 | } 295 | 296 | /// Commits all changes since the last snapshot. Of course, they 297 | /// can still be undone if there is a snapshot further out. 298 | pub fn commit(&mut self, snapshot: Snapshot) { 299 | self.undo_log.commit(snapshot.snapshot); 300 | } 301 | } 302 | 303 | impl, L> ops::Deref for SnapshotVec { 304 | type Target = [D::Value]; 305 | fn deref(&self) -> &[D::Value] { 306 | self.values.as_ref() 307 | } 308 | } 309 | 310 | impl, L> ops::DerefMut for SnapshotVec { 311 | fn deref_mut(&mut self) -> &mut [D::Value] { 312 | self.values.as_mut() 313 | } 314 | } 315 | 316 | impl, L> ops::Index for SnapshotVec { 317 | type Output = D::Value; 318 | fn index(&self, index: usize) -> &D::Value { 319 | self.get(index) 320 | } 321 | } 322 | 323 | impl, L> ops::IndexMut for SnapshotVec { 324 | fn index_mut(&mut self, index: usize) -> &mut D::Value { 325 | self.get_mut(index) 326 | } 327 | } 328 | 329 | impl + Extend> Extend 330 | for SnapshotVec 331 | { 332 | fn extend(&mut self, iterable: T) 333 | where 334 | T: IntoIterator, 335 | { 336 | let initial_len = self.values.len(); 337 | self.values.extend(iterable); 338 | let final_len = self.values.len(); 339 | 340 | if self.in_snapshot() { 341 | self.undo_log 342 | .extend((initial_len..final_len).map(|len| NewElem(len))); 343 | } 344 | } 345 | } 346 | 347 | impl Clone for SnapshotVec 348 | where 349 | V: VecLike + Clone, 350 | L: Clone, 351 | { 352 | fn clone(&self) -> Self { 353 | SnapshotVec { 354 | values: self.values.clone(), 355 | undo_log: self.undo_log.clone(), 356 | _marker: PhantomData, 357 | } 358 | } 359 | } 360 | 361 | impl Clone for UndoLog 362 | where 363 | D::Value: Clone, 364 | D::Undo: Clone, 365 | { 366 | fn clone(&self) -> Self { 367 | match *self { 368 | NewElem(i) => NewElem(i), 369 | SetElem(i, ref v) => SetElem(i, v.clone()), 370 | Other(ref u) => Other(u.clone()), 371 | } 372 | } 373 | } 374 | 375 | impl SnapshotVecDelegate for i32 { 376 | type Value = i32; 377 | type Undo = (); 378 | 379 | fn reverse(_: &mut Vec, _: ()) {} 380 | } 381 | 382 | #[test] 383 | fn basic() { 384 | let mut vec: SnapshotVec = SnapshotVec::default(); 385 | assert!(!vec.in_snapshot()); 386 | assert_eq!(vec.len(), 0); 387 | vec.push(22); 388 | vec.push(33); 389 | assert_eq!(vec.len(), 2); 390 | assert_eq!(*vec.get(0), 22); 391 | assert_eq!(*vec.get(1), 33); 392 | vec.set(1, 34); 393 | assert_eq!(vec.len(), 2); 394 | assert_eq!(*vec.get(0), 22); 395 | assert_eq!(*vec.get(1), 34); 396 | 397 | let snapshot = vec.start_snapshot(); 398 | assert!(vec.in_snapshot()); 399 | 400 | vec.push(44); 401 | vec.push(55); 402 | vec.set(1, 35); 403 | assert_eq!(vec.len(), 4); 404 | assert_eq!(*vec.get(0), 22); 405 | assert_eq!(*vec.get(1), 35); 406 | assert_eq!(*vec.get(2), 44); 407 | assert_eq!(*vec.get(3), 55); 408 | 409 | vec.rollback_to(snapshot); 410 | assert!(!vec.in_snapshot()); 411 | 412 | assert_eq!(vec.len(), 2); 413 | assert_eq!(*vec.get(0), 22); 414 | assert_eq!(*vec.get(1), 34); 415 | } 416 | 417 | #[test] 418 | #[should_panic] 419 | fn out_of_order() { 420 | let mut vec: SnapshotVec = SnapshotVec::default(); 421 | vec.push(22); 422 | let snapshot1 = vec.start_snapshot(); 423 | vec.push(33); 424 | let snapshot2 = vec.start_snapshot(); 425 | vec.push(44); 426 | vec.rollback_to(snapshot1); // bogus, but accepted 427 | vec.rollback_to(snapshot2); // asserts 428 | } 429 | 430 | #[test] 431 | fn nested_commit_then_rollback() { 432 | let mut vec: SnapshotVec = SnapshotVec::default(); 433 | vec.push(22); 434 | let snapshot1 = vec.start_snapshot(); 435 | let snapshot2 = vec.start_snapshot(); 436 | vec.set(0, 23); 437 | vec.commit(snapshot2); 438 | assert_eq!(*vec.get(0), 23); 439 | vec.rollback_to(snapshot1); 440 | assert_eq!(*vec.get(0), 22); 441 | } 442 | -------------------------------------------------------------------------------- /src/undo_log.rs: -------------------------------------------------------------------------------- 1 | //! Module which contains the snapshot/rollback functionality of the `ena` data structures. 2 | //! 3 | //! For most usecases this is just an internal implementation detail. However if many `ena` 4 | //! data structures are snapshotted simultaneously it is possible to use 5 | //! `UnificationTableStorage`/`SnapshotVecStorage` instead and use a custom `UndoLogs` 6 | //! type capable of recording the actions of all used data structures. 7 | //! 8 | //! Since the `*Storage` variants do not have an undo log `with_log` must be called with the 9 | //! unified log before any mutating actions. 10 | 11 | /// A trait which allows undo actions (`T`) to be pushed which can be used to rollback actions at a 12 | /// later time if needed. 13 | /// 14 | /// The undo actions themselves are opaque to `UndoLogs`, only specified `Rollback` implementations 15 | /// need to know what an action is and how to reverse it. 16 | pub trait UndoLogs { 17 | /// True if a snapshot has started, false otherwise 18 | fn in_snapshot(&self) -> bool { 19 | self.num_open_snapshots() > 0 20 | } 21 | 22 | /// How many open snapshots this undo log currently has 23 | fn num_open_snapshots(&self) -> usize; 24 | 25 | /// Pushes a new "undo item" onto the undo log. This method is invoked when some action is taken 26 | /// (e.g., a variable is unified). It records the info needed to reverse that action should an 27 | /// enclosing snapshot be rolled back. 28 | fn push(&mut self, undo: T); 29 | 30 | /// Removes all items from the undo log. 31 | fn clear(&mut self); 32 | 33 | /// Extends the undo log with many undos. 34 | fn extend(&mut self, undos: I) 35 | where 36 | Self: Sized, 37 | I: IntoIterator, 38 | { 39 | undos.into_iter().for_each(|undo| self.push(undo)); 40 | } 41 | } 42 | 43 | impl<'a, T, U> UndoLogs for &'a mut U 44 | where 45 | U: UndoLogs, 46 | { 47 | fn in_snapshot(&self) -> bool { 48 | U::in_snapshot(self) 49 | } 50 | fn num_open_snapshots(&self) -> usize { 51 | U::num_open_snapshots(self) 52 | } 53 | fn push(&mut self, undo: T) { 54 | U::push(self, undo) 55 | } 56 | fn clear(&mut self) { 57 | U::clear(self); 58 | } 59 | fn extend(&mut self, undos: I) 60 | where 61 | I: IntoIterator, 62 | { 63 | U::extend(self, undos) 64 | } 65 | } 66 | 67 | /// A trait which extends `UndoLogs` to allow snapshots to be done at specific points. Each snapshot can then be used to 68 | /// rollback any changes to an underlying data structures if they were not desirable. 69 | /// 70 | /// Each snapshot must be consumed linearly with either `rollback_to` or `commit`. 71 | pub trait Snapshots: UndoLogs { 72 | type Snapshot; 73 | 74 | /// Returns true if `self` has made any changes since snapshot started. 75 | fn has_changes(&self, snapshot: &Self::Snapshot) -> bool { 76 | !self.actions_since_snapshot(snapshot).is_empty() 77 | } 78 | 79 | /// Returns the slice of actions that were taken since the snapshot began. 80 | fn actions_since_snapshot(&self, snapshot: &Self::Snapshot) -> &[T]; 81 | 82 | /// Starts a new snapshot. That snapshot must eventually either be committed via a call to 83 | /// commit or rollback via rollback_to. Snapshots can be nested (i.e., you can start a snapshot 84 | /// whilst another snapshot is in progress) but you must then commit or rollback the inner 85 | /// snapshot before attempting to commit or rollback the outer snapshot. 86 | fn start_snapshot(&mut self) -> Self::Snapshot; 87 | 88 | /// Rollback (undo) the changes made to `storage` since the snapshot. 89 | fn rollback_to(&mut self, storage: impl FnOnce() -> R, snapshot: Self::Snapshot) 90 | where 91 | R: Rollback; 92 | 93 | /// Commit: keep the changes that have been made since the snapshot began 94 | fn commit(&mut self, snapshot: Self::Snapshot); 95 | } 96 | 97 | impl Snapshots for &'_ mut U 98 | where 99 | U: Snapshots, 100 | { 101 | type Snapshot = U::Snapshot; 102 | fn has_changes(&self, snapshot: &Self::Snapshot) -> bool { 103 | U::has_changes(self, snapshot) 104 | } 105 | fn actions_since_snapshot(&self, snapshot: &Self::Snapshot) -> &[T] { 106 | U::actions_since_snapshot(self, snapshot) 107 | } 108 | 109 | fn start_snapshot(&mut self) -> Self::Snapshot { 110 | U::start_snapshot(self) 111 | } 112 | fn rollback_to(&mut self, storage: impl FnOnce() -> R, snapshot: Self::Snapshot) 113 | where 114 | R: Rollback, 115 | { 116 | U::rollback_to(self, storage, snapshot) 117 | } 118 | 119 | fn commit(&mut self, snapshot: Self::Snapshot) { 120 | U::commit(self, snapshot) 121 | } 122 | } 123 | 124 | pub struct NoUndo; 125 | impl UndoLogs for NoUndo { 126 | fn num_open_snapshots(&self) -> usize { 127 | 0 128 | } 129 | fn push(&mut self, _undo: T) {} 130 | fn clear(&mut self) {} 131 | } 132 | 133 | /// A basic undo log. 134 | #[derive(Clone, Debug)] 135 | pub struct VecLog { 136 | log: Vec, 137 | num_open_snapshots: usize, 138 | } 139 | 140 | impl Default for VecLog { 141 | fn default() -> Self { 142 | VecLog { 143 | log: Vec::new(), 144 | num_open_snapshots: 0, 145 | } 146 | } 147 | } 148 | 149 | impl UndoLogs for VecLog { 150 | fn num_open_snapshots(&self) -> usize { 151 | self.num_open_snapshots 152 | } 153 | fn push(&mut self, undo: T) { 154 | self.log.push(undo); 155 | } 156 | fn clear(&mut self) { 157 | self.log.clear(); 158 | self.num_open_snapshots = 0; 159 | } 160 | } 161 | 162 | impl Snapshots for VecLog { 163 | type Snapshot = Snapshot; 164 | 165 | fn has_changes(&self, snapshot: &Self::Snapshot) -> bool { 166 | self.log.len() > snapshot.undo_len 167 | } 168 | fn actions_since_snapshot(&self, snapshot: &Snapshot) -> &[T] { 169 | &self.log[snapshot.undo_len..] 170 | } 171 | 172 | fn start_snapshot(&mut self) -> Snapshot { 173 | self.num_open_snapshots += 1; 174 | Snapshot { 175 | undo_len: self.log.len(), 176 | } 177 | } 178 | 179 | fn rollback_to(&mut self, values: impl FnOnce() -> R, snapshot: Snapshot) 180 | where 181 | R: Rollback, 182 | { 183 | debug!("rollback_to({})", snapshot.undo_len); 184 | 185 | self.assert_open_snapshot(&snapshot); 186 | 187 | if self.log.len() > snapshot.undo_len { 188 | let mut values = values(); 189 | while self.log.len() > snapshot.undo_len { 190 | values.reverse(self.log.pop().unwrap()); 191 | } 192 | } 193 | 194 | self.num_open_snapshots -= 1; 195 | } 196 | 197 | fn commit(&mut self, snapshot: Snapshot) { 198 | debug!("commit({})", snapshot.undo_len); 199 | 200 | self.assert_open_snapshot(&snapshot); 201 | 202 | if self.num_open_snapshots == 1 { 203 | // The root snapshot. It's safe to clear the undo log because 204 | // there's no snapshot further out that we might need to roll back 205 | // to. 206 | assert!(snapshot.undo_len == 0); 207 | self.log.clear(); 208 | } 209 | 210 | self.num_open_snapshots -= 1; 211 | } 212 | } 213 | 214 | impl VecLog { 215 | fn assert_open_snapshot(&self, snapshot: &Snapshot) { 216 | // Failures here may indicate a failure to follow a stack discipline. 217 | assert!(self.log.len() >= snapshot.undo_len); 218 | assert!(self.num_open_snapshots > 0); 219 | } 220 | } 221 | 222 | impl std::ops::Index for VecLog { 223 | type Output = T; 224 | fn index(&self, key: usize) -> &T { 225 | &self.log[key] 226 | } 227 | } 228 | 229 | /// A trait implemented for storage types (like `SnapshotVecStorage`) which can be rolled back using actions of type `U`. 230 | pub trait Rollback { 231 | fn reverse(&mut self, undo: U); 232 | } 233 | 234 | impl Rollback for &'_ mut T 235 | where 236 | T: Rollback, 237 | { 238 | fn reverse(&mut self, undo: U) { 239 | T::reverse(self, undo) 240 | } 241 | } 242 | 243 | /// Snapshots are tokens that should be created/consumed linearly. 244 | pub struct Snapshot { 245 | // Length of the undo log at the time the snapshot was taken. 246 | undo_len: usize, 247 | } 248 | -------------------------------------------------------------------------------- /src/unify/backing_vec.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "persistent")] 2 | use dogged::DVec; 3 | use snapshot_vec as sv; 4 | use std::marker::PhantomData; 5 | use std::ops::{self, Range}; 6 | 7 | use undo_log::{Rollback, Snapshots, UndoLogs, VecLog}; 8 | 9 | use super::{UnifyKey, UnifyValue, VarValue}; 10 | 11 | #[allow(dead_code)] // rustc BUG 12 | #[allow(type_alias_bounds)] 13 | type Key = ::Key; 14 | 15 | /// Largely internal trait implemented by the unification table 16 | /// backing store types. The most common such type is `InPlace`, 17 | /// which indicates a standard, mutable unification table. 18 | pub trait UnificationStoreBase: ops::Index>> { 19 | type Key: UnifyKey; 20 | type Value: UnifyValue; 21 | 22 | fn len(&self) -> usize; 23 | 24 | fn tag() -> &'static str { 25 | Self::Key::tag() 26 | } 27 | } 28 | 29 | pub trait UnificationStoreMut: UnificationStoreBase { 30 | fn reset_unifications(&mut self, value: impl FnMut(u32) -> VarValue); 31 | 32 | fn push(&mut self, value: VarValue); 33 | 34 | fn reserve(&mut self, num_new_values: usize); 35 | 36 | fn update(&mut self, index: usize, op: F) 37 | where 38 | F: FnOnce(&mut VarValue); 39 | } 40 | 41 | pub trait UnificationStore: UnificationStoreMut { 42 | type Snapshot; 43 | 44 | fn start_snapshot(&mut self) -> Self::Snapshot; 45 | 46 | fn rollback_to(&mut self, snapshot: Self::Snapshot); 47 | 48 | fn commit(&mut self, snapshot: Self::Snapshot); 49 | 50 | fn values_since_snapshot(&self, snapshot: &Self::Snapshot) -> Range; 51 | } 52 | 53 | /// Backing store for an in-place unification table. 54 | /// Not typically used directly. 55 | #[derive(Clone, Debug)] 56 | pub struct InPlace< 57 | K: UnifyKey, 58 | V: sv::VecLike> = Vec>, 59 | L = VecLog>>, 60 | > { 61 | pub(crate) values: sv::SnapshotVec, V, L>, 62 | } 63 | 64 | // HACK(eddyb) manual impl avoids `Default` bound on `K`. 65 | impl> + Default, L: Default> Default for InPlace { 66 | fn default() -> Self { 67 | InPlace { 68 | values: sv::SnapshotVec::new(), 69 | } 70 | } 71 | } 72 | 73 | impl UnificationStoreBase for InPlace 74 | where 75 | K: UnifyKey, 76 | V: sv::VecLike>, 77 | { 78 | type Key = K; 79 | type Value = K::Value; 80 | 81 | fn len(&self) -> usize { 82 | self.values.len() 83 | } 84 | } 85 | 86 | impl UnificationStoreMut for InPlace 87 | where 88 | K: UnifyKey, 89 | V: sv::VecLike>, 90 | L: UndoLogs>>, 91 | { 92 | #[inline] 93 | fn reset_unifications(&mut self, mut value: impl FnMut(u32) -> VarValue) { 94 | self.values.set_all(|i| value(i as u32)); 95 | } 96 | 97 | #[inline] 98 | fn push(&mut self, value: VarValue) { 99 | self.values.push(value); 100 | } 101 | 102 | #[inline] 103 | fn reserve(&mut self, num_new_values: usize) { 104 | self.values.reserve(num_new_values); 105 | } 106 | 107 | #[inline] 108 | fn update(&mut self, index: usize, op: F) 109 | where 110 | F: FnOnce(&mut VarValue), 111 | { 112 | self.values.update(index, op) 113 | } 114 | } 115 | 116 | impl UnificationStore for InPlace 117 | where 118 | K: UnifyKey, 119 | V: sv::VecLike>, 120 | L: Snapshots>>, 121 | { 122 | type Snapshot = sv::Snapshot; 123 | 124 | #[inline] 125 | fn start_snapshot(&mut self) -> Self::Snapshot { 126 | self.values.start_snapshot() 127 | } 128 | 129 | #[inline] 130 | fn rollback_to(&mut self, snapshot: Self::Snapshot) { 131 | self.values.rollback_to(snapshot); 132 | } 133 | 134 | #[inline] 135 | fn commit(&mut self, snapshot: Self::Snapshot) { 136 | self.values.commit(snapshot); 137 | } 138 | 139 | #[inline] 140 | fn values_since_snapshot(&self, snapshot: &Self::Snapshot) -> Range { 141 | snapshot.value_count..self.len() 142 | } 143 | } 144 | 145 | impl ops::Index for InPlace 146 | where 147 | V: sv::VecLike>, 148 | K: UnifyKey, 149 | { 150 | type Output = VarValue; 151 | fn index(&self, index: usize) -> &VarValue { 152 | &self.values[index] 153 | } 154 | } 155 | 156 | #[doc(hidden)] 157 | #[derive(Copy, Clone, Debug)] 158 | pub struct Delegate(PhantomData); 159 | 160 | impl sv::SnapshotVecDelegate for Delegate { 161 | type Value = VarValue; 162 | type Undo = (); 163 | 164 | fn reverse(_: &mut Vec>, _: ()) {} 165 | } 166 | 167 | impl Rollback>> for super::UnificationTableStorage { 168 | fn reverse(&mut self, undo: sv::UndoLog>) { 169 | self.values.values.reverse(undo); 170 | } 171 | } 172 | 173 | #[cfg(feature = "persistent")] 174 | #[derive(Clone, Debug)] 175 | pub struct Persistent { 176 | values: DVec>, 177 | } 178 | 179 | // HACK(eddyb) manual impl avoids `Default` bound on `K`. 180 | #[cfg(feature = "persistent")] 181 | impl Default for Persistent { 182 | fn default() -> Self { 183 | Persistent { 184 | values: DVec::new(), 185 | } 186 | } 187 | } 188 | 189 | #[cfg(feature = "persistent")] 190 | impl UnificationStoreBase for Persistent { 191 | type Key = K; 192 | type Value = K::Value; 193 | 194 | fn len(&self) -> usize { 195 | self.values.len() 196 | } 197 | } 198 | 199 | #[cfg(feature = "persistent")] 200 | impl UnificationStoreMut for Persistent { 201 | #[inline] 202 | fn reset_unifications(&mut self, mut value: impl FnMut(u32) -> VarValue) { 203 | // Without extending dogged, there isn't obviously a more 204 | // efficient way to do this. But it's pretty dumb. Maybe 205 | // dogged needs a `map`. 206 | for i in 0..self.values.len() { 207 | self.values[i] = value(i as u32); 208 | } 209 | } 210 | 211 | #[inline] 212 | fn push(&mut self, value: VarValue) { 213 | self.values.push(value); 214 | } 215 | 216 | #[inline] 217 | fn reserve(&mut self, _num_new_values: usize) { 218 | // not obviously relevant to DVec. 219 | } 220 | 221 | #[inline] 222 | fn update(&mut self, index: usize, op: F) 223 | where 224 | F: FnOnce(&mut VarValue), 225 | { 226 | let p = &mut self.values[index]; 227 | op(p); 228 | } 229 | } 230 | 231 | #[cfg(feature = "persistent")] 232 | impl UnificationStore for Persistent { 233 | type Snapshot = Self; 234 | 235 | #[inline] 236 | fn start_snapshot(&mut self) -> Self::Snapshot { 237 | self.clone() 238 | } 239 | 240 | #[inline] 241 | fn rollback_to(&mut self, snapshot: Self::Snapshot) { 242 | *self = snapshot; 243 | } 244 | 245 | #[inline] 246 | fn commit(&mut self, _snapshot: Self::Snapshot) {} 247 | 248 | #[inline] 249 | fn values_since_snapshot(&self, snapshot: &Self::Snapshot) -> Range { 250 | snapshot.len()..self.len() 251 | } 252 | } 253 | 254 | #[cfg(feature = "persistent")] 255 | impl ops::Index for Persistent 256 | where 257 | K: UnifyKey, 258 | { 259 | type Output = VarValue; 260 | fn index(&self, index: usize) -> &VarValue { 261 | &self.values[index] 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/unify/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | //! Union-find implementation. The main type is `UnificationTable`. 12 | //! 13 | //! You can define your own type for the *keys* in the table, but you 14 | //! must implement `UnifyKey` for that type. The assumption is that 15 | //! keys will be newtyped integers, hence we require that they 16 | //! implement `Copy`. 17 | //! 18 | //! Keys can have values associated with them. The assumption is that 19 | //! these values are cheaply cloneable (ideally, `Copy`), and some of 20 | //! the interfaces are oriented around that assumption. If you just 21 | //! want the classical "union-find" algorithm where you group things 22 | //! into sets, use the `Value` type of `()`. 23 | //! 24 | //! When you have keys with non-trivial values, you must also define 25 | //! how those values can be merged. As part of doing this, you can 26 | //! define the "error" type to return on error; if errors are not 27 | //! possible, use `NoError` (an uninstantiable struct). Using this 28 | //! type also unlocks various more ergonomic methods (e.g., `union()` 29 | //! in place of `unify_var_var()`). 30 | //! 31 | //! The best way to see how it is used is to read the `tests.rs` file; 32 | //! search for e.g. `UnitKey`. 33 | 34 | use std::fmt::Debug; 35 | use std::marker; 36 | use std::ops::Range; 37 | 38 | use snapshot_vec::{self as sv, UndoLog}; 39 | use undo_log::{UndoLogs, VecLog}; 40 | 41 | mod backing_vec; 42 | pub use self::backing_vec::{ 43 | Delegate, InPlace, UnificationStore, UnificationStoreBase, UnificationStoreMut, 44 | }; 45 | 46 | #[cfg(feature = "persistent")] 47 | pub use self::backing_vec::Persistent; 48 | 49 | #[cfg(test)] 50 | mod tests; 51 | 52 | /// This trait is implemented by any type that can serve as a type 53 | /// variable. We call such variables *unification keys*. For example, 54 | /// this trait is implemented by `IntVid`, which represents integral 55 | /// variables. 56 | /// 57 | /// Each key type has an associated value type `V`. For example, for 58 | /// `IntVid`, this is `Option`, representing some 59 | /// (possibly not yet known) sort of integer. 60 | /// 61 | /// Clients are expected to provide implementations of this trait; you 62 | /// can see some examples in the `test` module. 63 | pub trait UnifyKey: Copy + Clone + Debug + PartialEq { 64 | type Value: UnifyValue; 65 | 66 | fn index(&self) -> u32; 67 | 68 | fn from_index(u: u32) -> Self; 69 | 70 | fn tag() -> &'static str; 71 | 72 | /// You should return first the key that should be used as root, 73 | /// then the other key (that will then point to the new root). 74 | /// 75 | /// NB. The only reason to implement this method is if you want to 76 | /// control what value is returned from `find()`. In general, it 77 | /// is better to let the unification table determine the root, 78 | /// since overriding the rank can cause execution time to increase 79 | /// dramatically. 80 | #[allow(unused_variables)] 81 | fn order_roots( 82 | a: Self, 83 | a_value: &Self::Value, 84 | b: Self, 85 | b_value: &Self::Value, 86 | ) -> Option<(Self, Self)> { 87 | None 88 | } 89 | } 90 | 91 | /// Trait implemented for **values** associated with a unification 92 | /// key. This trait defines how to merge the values from two keys that 93 | /// are unioned together. This merging can be fallible. If you attempt 94 | /// to union two keys whose values cannot be merged, then the error is 95 | /// propagated up and the two keys are not unioned. 96 | /// 97 | /// This crate provides implementations of `UnifyValue` for `()` 98 | /// (which is infallible) and `Option` (where `T: UnifyValue`). The 99 | /// option implementation merges two sum-values using the `UnifyValue` 100 | /// implementation of `T`. 101 | /// 102 | /// See also `EqUnifyValue`, which is a convenience trait for cases 103 | /// where the "merge" operation succeeds only if the two values are 104 | /// equal. 105 | pub trait UnifyValue: Clone + Debug { 106 | /// Defines the type to return when merging of two values fails. 107 | /// If merging is infallible, use the special struct `NoError` 108 | /// found in this crate, which unlocks various more convenient 109 | /// methods on the unification table. 110 | type Error; 111 | 112 | /// Given two values, produce a new value that combines them. 113 | /// If that is not possible, produce an error. 114 | fn unify_values(value1: &Self, value2: &Self) -> Result; 115 | } 116 | 117 | /// A convenient helper for unification values which must be equal or 118 | /// else an error occurs. For example, if you are unifying types in a 119 | /// simple functional language, this may be appropriate, since (e.g.) 120 | /// you can't unify a type variable bound to `int` with one bound to 121 | /// `float` (but you can unify two type variables both bound to 122 | /// `int`). 123 | /// 124 | /// Any type which implements `EqUnifyValue` automatially implements 125 | /// `UnifyValue`; if the two values are equal, merging is permitted. 126 | /// Otherwise, the error `(v1, v2)` is returned, where `v1` and `v2` 127 | /// are the two unequal values. 128 | pub trait EqUnifyValue: Eq + Clone + Debug {} 129 | 130 | impl UnifyValue for T { 131 | type Error = (T, T); 132 | 133 | fn unify_values(value1: &Self, value2: &Self) -> Result { 134 | if value1 == value2 { 135 | Ok(value1.clone()) 136 | } else { 137 | Err((value1.clone(), value2.clone())) 138 | } 139 | } 140 | } 141 | 142 | /// A struct which can never be instantiated. Used 143 | /// for the error type for infallible cases. 144 | #[derive(Debug)] 145 | pub struct NoError { 146 | _dummy: (), 147 | } 148 | 149 | /// Value of a unification key. We implement Tarjan's union-find 150 | /// algorithm: when two keys are unified, one of them is converted 151 | /// into a "redirect" pointing at the other. These redirects form a 152 | /// DAG: the roots of the DAG (nodes that are not redirected) are each 153 | /// associated with a value of type `V` and a rank. The rank is used 154 | /// to keep the DAG relatively balanced, which helps keep the running 155 | /// time of the algorithm under control. For more information, see 156 | /// . 157 | #[derive(PartialEq, Clone, Debug)] 158 | pub struct VarValue { 159 | parent: K, // if equal to self, this is a root 160 | value: K::Value, // value assigned (only relevant to root) 161 | rank: u32, // max depth (only relevant to root) 162 | } 163 | 164 | /// Table of unification keys and their values. You must define a key type K 165 | /// that implements the `UnifyKey` trait. Unification tables can be used in two-modes: 166 | /// 167 | /// - in-place (`UnificationTable>` or `InPlaceUnificationTable`): 168 | /// - This is the standard mutable mode, where the array is modified 169 | /// in place. 170 | /// - To do backtracking, you can employ the `snapshot` and `rollback_to` 171 | /// methods. 172 | /// - persistent (`UnificationTable>` or `PersistentUnificationTable`): 173 | /// - In this mode, we use a persistent vector to store the data, so that 174 | /// cloning the table is an O(1) operation. 175 | /// - This implies that ordinary operations are quite a bit slower though. 176 | /// - Requires the `persistent` feature be selected in your Cargo.toml file. 177 | #[derive(Clone, Debug, Default)] 178 | pub struct UnificationTable { 179 | /// Indicates the current value of each key. 180 | values: S, 181 | } 182 | 183 | pub type UnificationStorage = Vec>; 184 | pub type UnificationTableStorage = UnificationTable, ()>>; 185 | 186 | /// A unification table that uses an "in-place" vector. 187 | #[allow(type_alias_bounds)] 188 | pub type InPlaceUnificationTable< 189 | K: UnifyKey, 190 | V: sv::VecLike> = Vec>, 191 | L = VecLog>>, 192 | > = UnificationTable>; 193 | 194 | /// A unification table that uses a "persistent" vector. 195 | #[cfg(feature = "persistent")] 196 | #[allow(type_alias_bounds)] 197 | pub type PersistentUnificationTable = UnificationTable>; 198 | 199 | /// At any time, users may snapshot a unification table. The changes 200 | /// made during the snapshot may either be *committed* or *rolled back*. 201 | pub struct Snapshot { 202 | // Link snapshot to the unification store `S` of the table. 203 | marker: marker::PhantomData, 204 | snapshot: S::Snapshot, 205 | } 206 | 207 | impl VarValue { 208 | fn new_var(key: K, value: K::Value) -> VarValue { 209 | VarValue::new(key, value, 0) 210 | } 211 | 212 | fn new(parent: K, value: K::Value, rank: u32) -> VarValue { 213 | VarValue { 214 | parent: parent, // this is a root 215 | value: value, 216 | rank: rank, 217 | } 218 | } 219 | 220 | fn redirect(&mut self, to: K) { 221 | self.parent = to; 222 | } 223 | 224 | fn root(&mut self, rank: u32, value: K::Value) { 225 | self.rank = rank; 226 | self.value = value; 227 | } 228 | } 229 | 230 | impl UnificationTableStorage 231 | where 232 | K: UnifyKey, 233 | { 234 | /// Creates a `UnificationTable` using an external `undo_log`, allowing mutating methods to be 235 | /// called if `L` does not implement `UndoLogs` 236 | pub fn with_log<'a, L>( 237 | &'a mut self, 238 | undo_log: L, 239 | ) -> UnificationTable, L>> 240 | where 241 | L: UndoLogs>>, 242 | { 243 | UnificationTable { 244 | values: InPlace { 245 | values: self.values.values.with_log(undo_log), 246 | }, 247 | } 248 | } 249 | } 250 | 251 | // We can't use V:LatticeValue, much as I would like to, 252 | // because frequently the pattern is that V=Option for some 253 | // other type parameter U, and we have no way to say 254 | // Option:LatticeValue. 255 | 256 | impl UnificationTable { 257 | pub fn new() -> Self { 258 | Self::default() 259 | } 260 | } 261 | 262 | impl UnificationTable { 263 | /// Starts a new snapshot. Each snapshot must be either 264 | /// rolled back or committed in a "LIFO" (stack) order. 265 | pub fn snapshot(&mut self) -> Snapshot { 266 | Snapshot { 267 | marker: marker::PhantomData::, 268 | snapshot: self.values.start_snapshot(), 269 | } 270 | } 271 | 272 | /// Reverses all changes since the last snapshot. Also 273 | /// removes any keys that have been created since then. 274 | pub fn rollback_to(&mut self, snapshot: Snapshot) { 275 | debug!("{}: rollback_to()", S::tag()); 276 | self.values.rollback_to(snapshot.snapshot); 277 | } 278 | 279 | /// Commits all changes since the last snapshot. Of course, they 280 | /// can still be undone if there is a snapshot further out. 281 | pub fn commit(&mut self, snapshot: Snapshot) { 282 | debug!("{}: commit()", S::tag()); 283 | self.values.commit(snapshot.snapshot); 284 | } 285 | 286 | /// Returns the keys of all variables created since the `snapshot`. 287 | pub fn vars_since_snapshot(&self, snapshot: &Snapshot) -> Range { 288 | let range = self.values.values_since_snapshot(&snapshot.snapshot); 289 | S::Key::from_index(range.start as u32)..S::Key::from_index(range.end as u32) 290 | } 291 | } 292 | 293 | impl UnificationTable { 294 | /// Returns the number of keys created so far. 295 | pub fn len(&self) -> usize { 296 | self.values.len() 297 | } 298 | 299 | /// Obtains the current value for a particular key. 300 | /// Not for end-users; they can use `probe_value`. 301 | fn value(&self, key: S::Key) -> &VarValue { 302 | &self.values[key.index() as usize] 303 | } 304 | } 305 | 306 | impl UnificationTable { 307 | /// Creates a fresh key with the given value. 308 | pub fn new_key(&mut self, value: S::Value) -> S::Key { 309 | let len = self.values.len(); 310 | let key: S::Key = UnifyKey::from_index(len as u32); 311 | self.values.push(VarValue::new_var(key, value)); 312 | debug!("{}: created new key: {:?}", S::tag(), key); 313 | key 314 | } 315 | 316 | /// Reserve memory for `num_new_keys` to be created. Does not 317 | /// actually create the new keys; you must then invoke `new_key`. 318 | pub fn reserve(&mut self, num_new_keys: usize) { 319 | self.values.reserve(num_new_keys); 320 | } 321 | 322 | /// Clears all unifications that have been performed, resetting to 323 | /// the initial state. The values of each variable are given by 324 | /// the closure. 325 | pub fn reset_unifications(&mut self, mut value: impl FnMut(S::Key) -> S::Value) { 326 | self.values.reset_unifications(|i| { 327 | let key = UnifyKey::from_index(i as u32); 328 | let value = value(key); 329 | VarValue::new_var(key, value) 330 | }); 331 | } 332 | 333 | /// Find the root node for `vid`. This uses the standard 334 | /// union-find algorithm with path compression: 335 | /// . 336 | /// 337 | /// NB. This is a building-block operation and you would probably 338 | /// prefer to call `probe` below. 339 | /// 340 | /// This is an always-inlined version of this function for the hot 341 | /// callsites. `uninlined_get_root_key` is the never-inlined version. 342 | #[inline(always)] 343 | fn inlined_get_root_key(&mut self, vid: S::Key) -> S::Key { 344 | let v = self.value(vid); 345 | if v.parent == vid { 346 | return vid; 347 | } 348 | 349 | let redirect = v.parent; 350 | let root_key: S::Key = self.uninlined_get_root_key(redirect); 351 | if root_key != redirect { 352 | // Path compression 353 | self.update_value(vid, |value| value.parent = root_key); 354 | } 355 | 356 | root_key 357 | } 358 | 359 | // This is a never-inlined version of this function for cold callsites. 360 | // 'inlined_get_root_key` is the always-inlined version. 361 | #[inline(never)] 362 | fn uninlined_get_root_key(&mut self, vid: S::Key) -> S::Key { 363 | self.inlined_get_root_key(vid) 364 | } 365 | 366 | fn update_value(&mut self, key: S::Key, op: OP) 367 | where 368 | OP: FnOnce(&mut VarValue), 369 | { 370 | self.values.update(key.index() as usize, op); 371 | debug!("Updated variable {:?} to {:?}", key, self.value(key)); 372 | } 373 | 374 | /// Either redirects `node_a` to `node_b` or vice versa, depending 375 | /// on the relative rank. The value associated with the new root 376 | /// will be `new_value`. 377 | /// 378 | /// NB: This is the "union" operation of "union-find". It is 379 | /// really more of a building block. If the values associated with 380 | /// your key are non-trivial, you would probably prefer to call 381 | /// `unify_var_var` below. 382 | fn unify_roots(&mut self, key_a: S::Key, key_b: S::Key, new_value: S::Value) { 383 | debug!("unify(key_a={:?}, key_b={:?})", key_a, key_b); 384 | 385 | let rank_a = self.value(key_a).rank; 386 | let rank_b = self.value(key_b).rank; 387 | if let Some((new_root, redirected)) = S::Key::order_roots( 388 | key_a, 389 | &self.value(key_a).value, 390 | key_b, 391 | &self.value(key_b).value, 392 | ) { 393 | // compute the new rank for the new root that they chose; 394 | // this may not be the optimal choice. 395 | let new_rank = if new_root == key_a { 396 | debug_assert!(redirected == key_b); 397 | if rank_a > rank_b { 398 | rank_a 399 | } else { 400 | rank_b + 1 401 | } 402 | } else { 403 | debug_assert!(new_root == key_b); 404 | debug_assert!(redirected == key_a); 405 | if rank_b > rank_a { 406 | rank_b 407 | } else { 408 | rank_a + 1 409 | } 410 | }; 411 | self.redirect_root(new_rank, redirected, new_root, new_value); 412 | } else if rank_a > rank_b { 413 | // a has greater rank, so a should become b's parent, 414 | // i.e., b should redirect to a. 415 | self.redirect_root(rank_a, key_b, key_a, new_value); 416 | } else if rank_a < rank_b { 417 | // b has greater rank, so a should redirect to b. 418 | self.redirect_root(rank_b, key_a, key_b, new_value); 419 | } else { 420 | // If equal, redirect one to the other and increment the 421 | // other's rank. 422 | self.redirect_root(rank_a + 1, key_a, key_b, new_value); 423 | } 424 | } 425 | 426 | /// Internal method to redirect `old_root_key` (which is currently 427 | /// a root) to a child of `new_root_key` (which will remain a 428 | /// root). The rank and value of `new_root_key` will be updated to 429 | /// `new_rank` and `new_value` respectively. 430 | fn redirect_root( 431 | &mut self, 432 | new_rank: u32, 433 | old_root_key: S::Key, 434 | new_root_key: S::Key, 435 | new_value: S::Value, 436 | ) { 437 | self.update_value(old_root_key, |old_root_value| { 438 | old_root_value.redirect(new_root_key); 439 | }); 440 | self.update_value(new_root_key, |new_root_value| { 441 | new_root_value.root(new_rank, new_value); 442 | }); 443 | } 444 | } 445 | 446 | /// //////////////////////////////////////////////////////////////////////// 447 | /// Public API 448 | 449 | impl UnificationTable 450 | where 451 | S: UnificationStoreBase, 452 | K: UnifyKey, 453 | V: UnifyValue, 454 | { 455 | /// Obtains current value for key without any pointer chasing; may return `None` if key has been union'd. 456 | #[inline] 457 | pub fn try_probe_value<'a, K1>(&'a self, id: K1) -> Option<&'a V> 458 | where 459 | K1: Into, 460 | K: 'a, 461 | { 462 | let id = id.into(); 463 | let v = self.value(id); 464 | if v.parent == id { 465 | return Some(&v.value); 466 | } 467 | None 468 | } 469 | } 470 | 471 | impl UnificationTable 472 | where 473 | S: UnificationStoreMut, 474 | K: UnifyKey, 475 | V: UnifyValue, 476 | { 477 | /// Unions two keys without the possibility of failure; only 478 | /// applicable when unify values use `NoError` as their error 479 | /// type. 480 | pub fn union(&mut self, a_id: K1, b_id: K2) 481 | where 482 | K1: Into, 483 | K2: Into, 484 | V: UnifyValue, 485 | { 486 | self.unify_var_var(a_id, b_id).unwrap(); 487 | } 488 | 489 | /// Unions a key and a value without the possibility of failure; 490 | /// only applicable when unify values use `NoError` as their error 491 | /// type. 492 | pub fn union_value(&mut self, id: K1, value: V) 493 | where 494 | K1: Into, 495 | V: UnifyValue, 496 | { 497 | self.unify_var_value(id, value).unwrap(); 498 | } 499 | 500 | /// Given two keys, indicates whether they have been unioned together. 501 | pub fn unioned(&mut self, a_id: K1, b_id: K2) -> bool 502 | where 503 | K1: Into, 504 | K2: Into, 505 | { 506 | self.find(a_id) == self.find(b_id) 507 | } 508 | 509 | /// Given a key, returns the (current) root key. 510 | pub fn find(&mut self, id: K1) -> K 511 | where 512 | K1: Into, 513 | { 514 | let id = id.into(); 515 | self.uninlined_get_root_key(id) 516 | } 517 | 518 | /// Unions together two variables, merging their values. If 519 | /// merging the values fails, the error is propagated and this 520 | /// method has no effect. 521 | pub fn unify_var_var(&mut self, a_id: K1, b_id: K2) -> Result<(), V::Error> 522 | where 523 | K1: Into, 524 | K2: Into, 525 | { 526 | let a_id = a_id.into(); 527 | let b_id = b_id.into(); 528 | 529 | let root_a = self.uninlined_get_root_key(a_id); 530 | let root_b = self.uninlined_get_root_key(b_id); 531 | 532 | if root_a == root_b { 533 | return Ok(()); 534 | } 535 | 536 | let combined = V::unify_values(&self.value(root_a).value, &self.value(root_b).value)?; 537 | 538 | Ok(self.unify_roots(root_a, root_b, combined)) 539 | } 540 | 541 | /// Sets the value of the key `a_id` to `b`, attempting to merge 542 | /// with the previous value. 543 | pub fn unify_var_value(&mut self, a_id: K1, b: V) -> Result<(), V::Error> 544 | where 545 | K1: Into, 546 | { 547 | let a_id = a_id.into(); 548 | let root_a = self.uninlined_get_root_key(a_id); 549 | let value = V::unify_values(&self.value(root_a).value, &b)?; 550 | self.update_value(root_a, |node| node.value = value); 551 | Ok(()) 552 | } 553 | 554 | /// Returns the current value for the given key. If the key has 555 | /// been union'd, this will give the value from the current root. 556 | pub fn probe_value(&mut self, id: K1) -> V 557 | where 558 | K1: Into, 559 | { 560 | self.inlined_probe_value(id) 561 | } 562 | 563 | // An always-inlined version of `probe_value`, for hot callsites. 564 | #[inline(always)] 565 | pub fn inlined_probe_value(&mut self, id: K1) -> V 566 | where 567 | K1: Into, 568 | { 569 | let id = id.into(); 570 | let id = self.inlined_get_root_key(id); 571 | self.value(id).value.clone() 572 | } 573 | } 574 | 575 | /////////////////////////////////////////////////////////////////////////// 576 | 577 | impl UnifyValue for () { 578 | type Error = NoError; 579 | 580 | fn unify_values(_: &(), _: &()) -> Result<(), NoError> { 581 | Ok(()) 582 | } 583 | } 584 | 585 | impl UnifyValue for Option { 586 | type Error = V::Error; 587 | 588 | fn unify_values(a: &Option, b: &Option) -> Result { 589 | match (a, b) { 590 | (&None, &None) => Ok(None), 591 | (&Some(ref v), &None) | (&None, &Some(ref v)) => Ok(Some(v.clone())), 592 | (&Some(ref a), &Some(ref b)) => match V::unify_values(a, b) { 593 | Ok(v) => Ok(Some(v)), 594 | Err(err) => Err(err), 595 | }, 596 | } 597 | } 598 | } 599 | -------------------------------------------------------------------------------- /src/unify/tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | // Naming the benchmarks using uppercase letters helps them sort 12 | // better. 13 | #![allow(non_snake_case)] 14 | 15 | #[cfg(feature = "bench")] 16 | extern crate test; 17 | #[cfg(feature = "bench")] 18 | use self::test::Bencher; 19 | use std::cmp; 20 | #[cfg(feature = "persistent")] 21 | use unify::Persistent; 22 | use unify::{EqUnifyValue, InPlace, InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; 23 | use unify::{UnificationStore, UnificationTable}; 24 | 25 | #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] 26 | struct UnitKey(u32); 27 | 28 | impl UnifyKey for UnitKey { 29 | type Value = (); 30 | fn index(&self) -> u32 { 31 | self.0 32 | } 33 | fn from_index(u: u32) -> UnitKey { 34 | UnitKey(u) 35 | } 36 | fn tag() -> &'static str { 37 | "UnitKey" 38 | } 39 | } 40 | 41 | macro_rules! all_modes { 42 | ($name:ident for $t:ty => $body:tt) => { 43 | fn test_body< 44 | $name: Clone + Default + UnificationStore::Value>, 45 | >() { 46 | $body 47 | } 48 | 49 | test_body::>(); 50 | 51 | #[cfg(feature = "persistent")] 52 | test_body::>(); 53 | }; 54 | } 55 | 56 | #[test] 57 | fn basic() { 58 | all_modes! { 59 | S for UnitKey => { 60 | let mut ut: UnificationTable = UnificationTable::new(); 61 | let k1 = ut.new_key(()); 62 | let k2 = ut.new_key(()); 63 | assert_eq!(ut.unioned(k1, k2), false); 64 | ut.union(k1, k2); 65 | assert_eq!(ut.unioned(k1, k2), true); 66 | } 67 | } 68 | } 69 | 70 | #[test] 71 | fn big_array() { 72 | all_modes! { 73 | S for UnitKey => { 74 | let mut ut: UnificationTable = UnificationTable::new(); 75 | let mut keys = Vec::new(); 76 | const MAX: usize = 1 << 15; 77 | 78 | for _ in 0..MAX { 79 | keys.push(ut.new_key(())); 80 | } 81 | 82 | for i in 1..MAX { 83 | let l = keys[i - 1]; 84 | let r = keys[i]; 85 | ut.union(l, r); 86 | } 87 | 88 | for i in 0..MAX { 89 | assert!(ut.unioned(keys[0], keys[i])); 90 | } 91 | } 92 | } 93 | } 94 | 95 | #[cfg(feature = "bench")] 96 | fn big_array_bench_generic>( 97 | b: &mut Bencher, 98 | ) { 99 | let mut ut: UnificationTable = UnificationTable::new(); 100 | let mut keys = Vec::new(); 101 | const MAX: usize = 1 << 15; 102 | 103 | for _ in 0..MAX { 104 | keys.push(ut.new_key(())); 105 | } 106 | 107 | b.iter(|| { 108 | for i in 1..MAX { 109 | let l = keys[i - 1]; 110 | let r = keys[i]; 111 | ut.union(l, r); 112 | } 113 | 114 | for i in 0..MAX { 115 | assert!(ut.unioned(keys[0], keys[i])); 116 | } 117 | }) 118 | } 119 | 120 | #[cfg(feature = "bench")] 121 | #[bench] 122 | fn big_array_bench_InPlace(b: &mut Bencher) { 123 | big_array_bench_generic::>(b); 124 | } 125 | 126 | #[cfg(all(feature = "bench", feature = "persistent"))] 127 | #[bench] 128 | fn big_array_bench_Persistent(b: &mut Bencher) { 129 | big_array_bench_generic::>(b); 130 | } 131 | 132 | #[cfg(feature = "bench")] 133 | fn big_array_bench_in_snapshot_generic>( 134 | b: &mut Bencher, 135 | ) { 136 | let mut ut: UnificationTable = UnificationTable::new(); 137 | let mut keys = Vec::new(); 138 | const MAX: usize = 1 << 15; 139 | 140 | for _ in 0..MAX { 141 | keys.push(ut.new_key(())); 142 | } 143 | 144 | b.iter(|| { 145 | let snapshot = ut.snapshot(); 146 | 147 | for i in 1..MAX { 148 | let l = keys[i - 1]; 149 | let r = keys[i]; 150 | ut.union(l, r); 151 | } 152 | 153 | for i in 0..MAX { 154 | assert!(ut.unioned(keys[0], keys[i])); 155 | } 156 | 157 | ut.rollback_to(snapshot); 158 | }) 159 | } 160 | 161 | #[cfg(feature = "bench")] 162 | #[bench] 163 | fn big_array_bench_in_snapshot_InPlace(b: &mut Bencher) { 164 | big_array_bench_in_snapshot_generic::>(b); 165 | } 166 | 167 | #[cfg(all(feature = "bench", feature = "persistent"))] 168 | #[bench] 169 | fn big_array_bench_in_snapshot_Persistent(b: &mut Bencher) { 170 | big_array_bench_in_snapshot_generic::>(b); 171 | } 172 | 173 | #[cfg(feature = "bench")] 174 | fn big_array_bench_clone_generic< 175 | S: Default + Clone + UnificationStore, 176 | >( 177 | b: &mut Bencher, 178 | ) { 179 | let mut ut: UnificationTable = UnificationTable::new(); 180 | let mut keys = Vec::new(); 181 | const MAX: usize = 1 << 15; 182 | 183 | for _ in 0..MAX { 184 | keys.push(ut.new_key(())); 185 | } 186 | 187 | b.iter(|| { 188 | let saved_table = ut.clone(); 189 | 190 | for i in 1..MAX { 191 | let l = keys[i - 1]; 192 | let r = keys[i]; 193 | ut.union(l, r); 194 | } 195 | 196 | for i in 0..MAX { 197 | assert!(ut.unioned(keys[0], keys[i])); 198 | } 199 | 200 | ut = saved_table; 201 | }) 202 | } 203 | 204 | #[cfg(feature = "bench")] 205 | #[bench] 206 | fn big_array_bench_clone_InPlace(b: &mut Bencher) { 207 | big_array_bench_clone_generic::>(b); 208 | } 209 | 210 | #[cfg(all(feature = "bench", feature = "persistent"))] 211 | #[bench] 212 | fn big_array_bench_clone_Persistent(b: &mut Bencher) { 213 | big_array_bench_clone_generic::>(b); 214 | } 215 | 216 | #[test] 217 | fn even_odd() { 218 | all_modes! { 219 | S for UnitKey => { 220 | let mut ut: UnificationTable = UnificationTable::new(); 221 | let mut keys = Vec::new(); 222 | const MAX: usize = 1 << 10; 223 | 224 | for i in 0..MAX { 225 | let key = ut.new_key(()); 226 | keys.push(key); 227 | 228 | if i >= 2 { 229 | ut.union(key, keys[i - 2]); 230 | } 231 | } 232 | 233 | for i in 1..MAX { 234 | assert!(!ut.unioned(keys[i - 1], keys[i])); 235 | } 236 | 237 | for i in 2..MAX { 238 | assert!(ut.unioned(keys[i - 2], keys[i])); 239 | } 240 | } 241 | } 242 | } 243 | 244 | #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] 245 | struct IntKey(u32); 246 | 247 | impl UnifyKey for IntKey { 248 | type Value = Option; 249 | fn index(&self) -> u32 { 250 | self.0 251 | } 252 | fn from_index(u: u32) -> IntKey { 253 | IntKey(u) 254 | } 255 | fn tag() -> &'static str { 256 | "IntKey" 257 | } 258 | } 259 | 260 | impl EqUnifyValue for i32 {} 261 | 262 | #[test] 263 | fn unify_same_int_twice() { 264 | all_modes! { 265 | S for IntKey => { 266 | let mut ut: UnificationTable = UnificationTable::new(); 267 | let k1 = ut.new_key(None); 268 | let k2 = ut.new_key(None); 269 | assert!(ut.unify_var_value(k1, Some(22)).is_ok()); 270 | assert!(ut.unify_var_value(k2, Some(22)).is_ok()); 271 | assert!(ut.unify_var_var(k1, k2).is_ok()); 272 | assert_eq!(ut.probe_value(k1), Some(22)); 273 | } 274 | } 275 | } 276 | 277 | #[test] 278 | fn unify_vars_then_int_indirect() { 279 | all_modes! { 280 | S for IntKey => { 281 | let mut ut: UnificationTable = UnificationTable::new(); 282 | let k1 = ut.new_key(None); 283 | let k2 = ut.new_key(None); 284 | assert!(ut.unify_var_var(k1, k2).is_ok()); 285 | assert!(ut.unify_var_value(k1, Some(22)).is_ok()); 286 | assert_eq!(ut.probe_value(k2), Some(22)); 287 | } 288 | } 289 | } 290 | 291 | #[test] 292 | fn unify_vars_different_ints_1() { 293 | all_modes! { 294 | S for IntKey => { 295 | let mut ut: UnificationTable = UnificationTable::new(); 296 | let k1 = ut.new_key(None); 297 | let k2 = ut.new_key(None); 298 | assert!(ut.unify_var_var(k1, k2).is_ok()); 299 | assert!(ut.unify_var_value(k1, Some(22)).is_ok()); 300 | assert!(ut.unify_var_value(k2, Some(23)).is_err()); 301 | } 302 | } 303 | } 304 | 305 | #[test] 306 | fn unify_vars_different_ints_2() { 307 | all_modes! { 308 | S for IntKey => { 309 | let mut ut: UnificationTable = UnificationTable::new(); 310 | let k1 = ut.new_key(None); 311 | let k2 = ut.new_key(None); 312 | assert!(ut.unify_var_var(k2, k1).is_ok()); 313 | assert!(ut.unify_var_value(k1, Some(22)).is_ok()); 314 | assert!(ut.unify_var_value(k2, Some(23)).is_err()); 315 | } 316 | } 317 | } 318 | 319 | #[test] 320 | fn unify_distinct_ints_then_vars() { 321 | all_modes! { 322 | S for IntKey => { 323 | let mut ut: UnificationTable = UnificationTable::new(); 324 | let k1 = ut.new_key(None); 325 | let k2 = ut.new_key(None); 326 | assert!(ut.unify_var_value(k1, Some(22)).is_ok()); 327 | assert!(ut.unify_var_value(k2, Some(23)).is_ok()); 328 | assert!(ut.unify_var_var(k2, k1).is_err()); 329 | } 330 | } 331 | } 332 | 333 | #[test] 334 | fn unify_root_value_1() { 335 | all_modes! { 336 | S for IntKey => { 337 | let mut ut: UnificationTable = UnificationTable::new(); 338 | let k1 = ut.new_key(None); 339 | let k2 = ut.new_key(None); 340 | let k3 = ut.new_key(None); 341 | assert!(ut.unify_var_value(k1, Some(22)).is_ok()); 342 | assert!(ut.unify_var_var(k1, k2).is_ok()); 343 | assert!(ut.unify_var_value(k3, Some(23)).is_ok()); 344 | assert!(ut.unify_var_var(k1, k3).is_err()); 345 | } 346 | } 347 | } 348 | 349 | #[test] 350 | fn unify_root_value_2() { 351 | all_modes! { 352 | S for IntKey => { 353 | let mut ut: UnificationTable = UnificationTable::new(); 354 | let k1 = ut.new_key(None); 355 | let k2 = ut.new_key(None); 356 | let k3 = ut.new_key(None); 357 | assert!(ut.unify_var_value(k1, Some(22)).is_ok()); 358 | assert!(ut.unify_var_var(k2, k1).is_ok()); 359 | assert!(ut.unify_var_value(k3, Some(23)).is_ok()); 360 | assert!(ut.unify_var_var(k1, k3).is_err()); 361 | } 362 | } 363 | } 364 | 365 | #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] 366 | struct OrderedKey(u32); 367 | 368 | #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] 369 | struct OrderedRank(u32); 370 | 371 | impl UnifyKey for OrderedKey { 372 | type Value = OrderedRank; 373 | fn index(&self) -> u32 { 374 | self.0 375 | } 376 | fn from_index(u: u32) -> OrderedKey { 377 | OrderedKey(u) 378 | } 379 | fn tag() -> &'static str { 380 | "OrderedKey" 381 | } 382 | fn order_roots( 383 | a: OrderedKey, 384 | a_rank: &OrderedRank, 385 | b: OrderedKey, 386 | b_rank: &OrderedRank, 387 | ) -> Option<(OrderedKey, OrderedKey)> { 388 | println!("{:?} vs {:?}", a_rank, b_rank); 389 | if a_rank > b_rank { 390 | Some((a, b)) 391 | } else if b_rank > a_rank { 392 | Some((b, a)) 393 | } else { 394 | None 395 | } 396 | } 397 | } 398 | 399 | impl UnifyValue for OrderedRank { 400 | type Error = NoError; 401 | 402 | fn unify_values(value1: &Self, value2: &Self) -> Result { 403 | Ok(OrderedRank(cmp::max(value1.0, value2.0))) 404 | } 405 | } 406 | 407 | #[test] 408 | fn ordered_key() { 409 | all_modes! { 410 | S for OrderedKey => { 411 | let mut ut: UnificationTable = UnificationTable::new(); 412 | 413 | let k0_1 = ut.new_key(OrderedRank(0)); 414 | let k0_2 = ut.new_key(OrderedRank(0)); 415 | let k0_3 = ut.new_key(OrderedRank(0)); 416 | let k0_4 = ut.new_key(OrderedRank(0)); 417 | 418 | ut.union(k0_1, k0_2); // rank of one of those will now be 1 419 | ut.union(k0_3, k0_4); // rank of new root also 1 420 | ut.union(k0_1, k0_3); // rank of new root now 2 421 | 422 | let k0_5 = ut.new_key(OrderedRank(0)); 423 | let k0_6 = ut.new_key(OrderedRank(0)); 424 | ut.union(k0_5, k0_6); // rank of new root now 1 425 | 426 | ut.union(k0_1, k0_5); // new root rank 2, should not be k0_5 or k0_6 427 | assert!(vec![k0_1, k0_2, k0_3, k0_4].contains(&ut.find(k0_1))); 428 | } 429 | } 430 | } 431 | 432 | #[test] 433 | fn ordered_key_k1() { 434 | all_modes! { 435 | S for UnitKey => { 436 | let mut ut: InPlaceUnificationTable = UnificationTable::new(); 437 | 438 | let k0_1 = ut.new_key(OrderedRank(0)); 439 | let k0_2 = ut.new_key(OrderedRank(0)); 440 | let k0_3 = ut.new_key(OrderedRank(0)); 441 | let k0_4 = ut.new_key(OrderedRank(0)); 442 | 443 | ut.union(k0_1, k0_2); // rank of one of those will now be 1 444 | ut.union(k0_3, k0_4); // rank of new root also 1 445 | ut.union(k0_1, k0_3); // rank of new root now 2 446 | 447 | let k1_5 = ut.new_key(OrderedRank(1)); 448 | let k1_6 = ut.new_key(OrderedRank(1)); 449 | ut.union(k1_5, k1_6); // rank of new root now 1 450 | 451 | ut.union(k0_1, k1_5); // even though k1 has lower rank, it wins 452 | assert!( 453 | vec![k1_5, k1_6].contains(&ut.find(k0_1)), 454 | "unexpected choice for root: {:?}", 455 | ut.find(k0_1) 456 | ); 457 | } 458 | } 459 | } 460 | 461 | /// Test that we *can* clone. 462 | #[test] 463 | fn clone_table() { 464 | all_modes! { 465 | S for IntKey => { 466 | let mut ut: UnificationTable = UnificationTable::new(); 467 | let k1 = ut.new_key(None); 468 | let k2 = ut.new_key(None); 469 | let k3 = ut.new_key(None); 470 | assert!(ut.unify_var_value(k1, Some(22)).is_ok()); 471 | assert!(ut.unify_var_value(k2, Some(22)).is_ok()); 472 | assert!(ut.unify_var_var(k1, k2).is_ok()); 473 | assert_eq!(ut.probe_value(k3), None); 474 | 475 | let mut ut1 = ut.clone(); 476 | assert_eq!(ut1.probe_value(k1), Some(22)); 477 | assert_eq!(ut1.probe_value(k3), None); 478 | 479 | assert!(ut.unify_var_value(k3, Some(44)).is_ok()); 480 | 481 | assert_eq!(ut1.probe_value(k1), Some(22)); 482 | assert_eq!(ut1.probe_value(k3), None); 483 | assert_eq!(ut.probe_value(k3), Some(44)); 484 | } 485 | } 486 | } 487 | -------------------------------------------------------------------------------- /tests/external_undo_log.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate ena; 4 | 5 | use ena::{ 6 | snapshot_vec as sv, 7 | undo_log::{Rollback, Snapshots, UndoLogs}, 8 | unify::{self as ut, EqUnifyValue, UnifyKey}, 9 | }; 10 | 11 | #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] 12 | struct IntKey(u32); 13 | 14 | impl UnifyKey for IntKey { 15 | type Value = Option; 16 | fn index(&self) -> u32 { 17 | self.0 18 | } 19 | fn from_index(u: u32) -> IntKey { 20 | IntKey(u) 21 | } 22 | fn tag() -> &'static str { 23 | "IntKey" 24 | } 25 | } 26 | 27 | impl EqUnifyValue for IntKey {} 28 | 29 | enum UndoLog { 30 | EqRelation(sv::UndoLog>), 31 | Values(sv::UndoLog), 32 | } 33 | 34 | impl From>> for UndoLog { 35 | fn from(l: sv::UndoLog>) -> Self { 36 | UndoLog::EqRelation(l) 37 | } 38 | } 39 | 40 | impl From> for UndoLog { 41 | fn from(l: sv::UndoLog) -> Self { 42 | UndoLog::Values(l) 43 | } 44 | } 45 | 46 | impl Rollback for TypeVariableStorage { 47 | fn reverse(&mut self, undo: UndoLog) { 48 | match undo { 49 | UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), 50 | UndoLog::Values(undo) => self.values.reverse(undo), 51 | } 52 | } 53 | } 54 | 55 | #[derive(Default)] 56 | struct TypeVariableStorage { 57 | values: sv::SnapshotVecStorage, 58 | 59 | eq_relations: ut::UnificationTableStorage, 60 | } 61 | 62 | impl TypeVariableStorage { 63 | fn with_log<'a>(&'a mut self, undo_log: &'a mut TypeVariableUndoLogs) -> TypeVariableTable<'a> { 64 | TypeVariableTable { 65 | storage: self, 66 | undo_log, 67 | } 68 | } 69 | 70 | fn len(&mut self) -> usize { 71 | assert_eq!(self.values.len(), self.eq_relations.len()); 72 | self.values.len() 73 | } 74 | } 75 | 76 | struct TypeVariableTable<'a> { 77 | storage: &'a mut TypeVariableStorage, 78 | 79 | undo_log: &'a mut TypeVariableUndoLogs, 80 | } 81 | 82 | impl TypeVariableTable<'_> { 83 | fn new(&mut self, i: i32) -> IntKey { 84 | self.storage.values.with_log(&mut self.undo_log).push(i); 85 | self.storage 86 | .eq_relations 87 | .with_log(&mut self.undo_log) 88 | .new_key(None) 89 | } 90 | } 91 | 92 | struct Snapshot { 93 | undo_len: usize, 94 | } 95 | 96 | struct TypeVariableUndoLogs { 97 | logs: Vec, 98 | num_open_snapshots: usize, 99 | } 100 | 101 | impl Default for TypeVariableUndoLogs { 102 | fn default() -> Self { 103 | Self { 104 | logs: Default::default(), 105 | num_open_snapshots: Default::default(), 106 | } 107 | } 108 | } 109 | 110 | impl UndoLogs for TypeVariableUndoLogs 111 | where 112 | UndoLog: From, 113 | { 114 | fn num_open_snapshots(&self) -> usize { 115 | self.num_open_snapshots 116 | } 117 | fn push(&mut self, undo: T) { 118 | if self.in_snapshot() { 119 | self.logs.push(undo.into()) 120 | } 121 | } 122 | fn clear(&mut self) { 123 | self.logs.clear(); 124 | self.num_open_snapshots = 0; 125 | } 126 | fn extend(&mut self, undos: J) 127 | where 128 | Self: Sized, 129 | J: IntoIterator, 130 | { 131 | if self.in_snapshot() { 132 | self.logs.extend(undos.into_iter().map(UndoLog::from)) 133 | } 134 | } 135 | } 136 | 137 | impl Snapshots for TypeVariableUndoLogs { 138 | type Snapshot = Snapshot; 139 | fn actions_since_snapshot(&self, snapshot: &Self::Snapshot) -> &[UndoLog] { 140 | &self.logs[snapshot.undo_len..] 141 | } 142 | 143 | fn start_snapshot(&mut self) -> Self::Snapshot { 144 | self.num_open_snapshots += 1; 145 | Snapshot { 146 | undo_len: self.logs.len(), 147 | } 148 | } 149 | 150 | fn rollback_to(&mut self, values: impl FnOnce() -> R, snapshot: Self::Snapshot) 151 | where 152 | R: Rollback, 153 | { 154 | debug!("rollback_to({})", snapshot.undo_len); 155 | 156 | if self.logs.len() > snapshot.undo_len { 157 | let mut values = values(); 158 | while self.logs.len() > snapshot.undo_len { 159 | values.reverse(self.logs.pop().unwrap()); 160 | } 161 | } 162 | 163 | if self.num_open_snapshots == 1 { 164 | // The root snapshot. It's safe to clear the undo log because 165 | // there's no snapshot further out that we might need to roll back 166 | // to. 167 | assert!(snapshot.undo_len == 0); 168 | self.logs.clear(); 169 | } 170 | 171 | self.num_open_snapshots -= 1; 172 | } 173 | 174 | fn commit(&mut self, snapshot: Self::Snapshot) { 175 | debug!("commit({})", snapshot.undo_len); 176 | 177 | if self.num_open_snapshots == 1 { 178 | // The root snapshot. It's safe to clear the undo log because 179 | // there's no snapshot further out that we might need to roll back 180 | // to. 181 | assert!(snapshot.undo_len == 0); 182 | self.logs.clear(); 183 | } 184 | 185 | self.num_open_snapshots -= 1; 186 | } 187 | } 188 | 189 | /// Tests that a undo log stored externally can be used with TypeVariableTable 190 | #[test] 191 | fn external_undo_log() { 192 | let mut storage = TypeVariableStorage::default(); 193 | let mut undo_log = TypeVariableUndoLogs::default(); 194 | 195 | let snapshot = undo_log.start_snapshot(); 196 | storage.with_log(&mut undo_log).new(1); 197 | storage.with_log(&mut undo_log).new(2); 198 | assert_eq!(storage.len(), 2); 199 | 200 | undo_log.rollback_to(|| &mut storage, snapshot); 201 | assert_eq!(storage.len(), 0); 202 | } 203 | --------------------------------------------------------------------------------