Mercurial > hg
comparison rust/hg-core/src/ancestors.rs @ 41054:ef54bd33b476
rust: core implementation for lazyancestors
Once exposed through appropriate bindings, this
should be able to replace ancestor.lazyancestors
entirely.
Differential Revision: https://phab.mercurial-scm.org/D5440
author | Georges Racinet <gracinet@anybox.fr> |
---|---|
date | Tue, 04 Dec 2018 11:05:06 +0100 |
parents | d097dd0afc19 |
children | 247f51cfc668 |
comparison
equal
deleted
inserted
replaced
41053:d9f439fcdb4c | 41054:ef54bd33b476 |
---|---|
21 pub struct AncestorsIterator<G: Graph> { | 21 pub struct AncestorsIterator<G: Graph> { |
22 graph: G, | 22 graph: G, |
23 visit: BinaryHeap<Revision>, | 23 visit: BinaryHeap<Revision>, |
24 seen: HashSet<Revision>, | 24 seen: HashSet<Revision>, |
25 stoprev: Revision, | 25 stoprev: Revision, |
26 } | |
27 | |
28 /// Lazy ancestors set, backed by AncestorsIterator | |
29 pub struct LazyAncestors<G: Graph + Clone> { | |
30 graph: G, | |
31 containsiter: AncestorsIterator<G>, | |
32 initrevs: Vec<Revision>, | |
33 stoprev: Revision, | |
34 inclusive: bool, | |
26 } | 35 } |
27 | 36 |
28 pub struct MissingAncestors<G: Graph> { | 37 pub struct MissingAncestors<G: Graph> { |
29 graph: G, | 38 graph: G, |
30 bases: HashSet<Revision>, | 39 bases: HashSet<Revision>, |
96 return Ok(false); | 105 return Ok(false); |
97 } | 106 } |
98 } | 107 } |
99 Ok(false) | 108 Ok(false) |
100 } | 109 } |
110 | |
111 pub fn peek(&self) -> Option<Revision> { | |
112 self.visit.peek().map(|&r| r) | |
113 } | |
114 | |
115 /// Tell if the iterator is about an empty set | |
116 /// | |
117 /// The result does not depend whether the iterator has been consumed | |
118 /// or not. | |
119 /// This is mostly meant for iterators backing a lazy ancestors set | |
120 pub fn is_empty(&self) -> bool { | |
121 if self.visit.len() > 0 { | |
122 return false; | |
123 } | |
124 if self.seen.len() > 1 { | |
125 return false; | |
126 } | |
127 // at this point, the seen set is at most a singleton. | |
128 // If not `self.inclusive`, it's still possible that it has only | |
129 // the null revision | |
130 self.seen.is_empty() || self.seen.contains(&NULL_REVISION) | |
131 } | |
101 } | 132 } |
102 | 133 |
103 /// Main implementation. | 134 /// Main implementation for the iterator |
104 /// | 135 /// |
105 /// The algorithm is the same as in `_lazyancestorsiter()` from `ancestors.py` | 136 /// The algorithm is the same as in `_lazyancestorsiter()` from `ancestors.py` |
106 /// with a few non crucial differences: | 137 /// with a few non crucial differences: |
107 /// | 138 /// |
108 /// - there's no filtering of invalid parent revisions. Actually, it should be | 139 /// - there's no filtering of invalid parent revisions. Actually, it should be |
132 self.seen.insert(p1); | 163 self.seen.insert(p1); |
133 }; | 164 }; |
134 | 165 |
135 self.conditionally_push_rev(p2); | 166 self.conditionally_push_rev(p2); |
136 Some(Ok(current)) | 167 Some(Ok(current)) |
168 } | |
169 } | |
170 | |
171 impl<G: Graph + Clone> LazyAncestors<G> { | |
172 pub fn new( | |
173 graph: G, | |
174 initrevs: impl IntoIterator<Item = Revision>, | |
175 stoprev: Revision, | |
176 inclusive: bool, | |
177 ) -> Result<Self, GraphError> { | |
178 let v: Vec<Revision> = initrevs.into_iter().collect(); | |
179 Ok(LazyAncestors { | |
180 graph: graph.clone(), | |
181 containsiter: AncestorsIterator::new( | |
182 graph, | |
183 v.iter().cloned(), | |
184 stoprev, | |
185 inclusive, | |
186 )?, | |
187 initrevs: v, | |
188 stoprev: stoprev, | |
189 inclusive: inclusive, | |
190 }) | |
191 } | |
192 | |
193 pub fn contains(&mut self, rev: Revision) -> Result<bool, GraphError> { | |
194 self.containsiter.contains(rev) | |
195 } | |
196 | |
197 pub fn is_empty(&self) -> bool { | |
198 self.containsiter.is_empty() | |
199 } | |
200 | |
201 pub fn iter(&self) -> AncestorsIterator<G> { | |
202 // the arguments being the same as for self.containsiter, we know | |
203 // for sure that AncestorsIterator constructor can't fail | |
204 AncestorsIterator::new( | |
205 self.graph.clone(), | |
206 self.initrevs.iter().cloned(), | |
207 self.stoprev, | |
208 self.inclusive, | |
209 ) | |
210 .unwrap() | |
137 } | 211 } |
138 } | 212 } |
139 | 213 |
140 impl<G: Graph> MissingAncestors<G> { | 214 impl<G: Graph> MissingAncestors<G> { |
141 pub fn new(graph: G, bases: impl IntoIterator<Item = Revision>) -> Self { | 215 pub fn new(graph: G, bases: impl IntoIterator<Item = Revision>) -> Self { |
405 let mut lazy = | 479 let mut lazy = |
406 AncestorsIterator::new(Stub, vec![0], 0, false).unwrap(); | 480 AncestorsIterator::new(Stub, vec![0], 0, false).unwrap(); |
407 assert!(!lazy.contains(NULL_REVISION).unwrap()); | 481 assert!(!lazy.contains(NULL_REVISION).unwrap()); |
408 } | 482 } |
409 | 483 |
484 #[test] | |
485 fn test_peek() { | |
486 let mut iter = | |
487 AncestorsIterator::new(Stub, vec![10], 0, true).unwrap(); | |
488 // peek() gives us the next value | |
489 assert_eq!(iter.peek(), Some(10)); | |
490 // but it's not been consumed | |
491 assert_eq!(iter.next(), Some(Ok(10))); | |
492 // and iteration resumes normally | |
493 assert_eq!(iter.next(), Some(Ok(5))); | |
494 | |
495 // let's drain the iterator to test peek() at the end | |
496 while iter.next().is_some() {} | |
497 assert_eq!(iter.peek(), None); | |
498 } | |
499 | |
500 #[test] | |
501 fn test_empty() { | |
502 let mut iter = | |
503 AncestorsIterator::new(Stub, vec![10], 0, true).unwrap(); | |
504 assert!(!iter.is_empty()); | |
505 while iter.next().is_some() {} | |
506 assert!(!iter.is_empty()); | |
507 | |
508 let iter = AncestorsIterator::new(Stub, vec![], 0, true).unwrap(); | |
509 assert!(iter.is_empty()); | |
510 | |
511 // case where iter.seen == {NULL_REVISION} | |
512 let iter = AncestorsIterator::new(Stub, vec![0], 0, false).unwrap(); | |
513 assert!(iter.is_empty()); | |
514 } | |
515 | |
410 /// A corrupted Graph, supporting error handling tests | 516 /// A corrupted Graph, supporting error handling tests |
517 #[derive(Clone, Debug)] | |
411 struct Corrupted; | 518 struct Corrupted; |
412 | 519 |
413 impl Graph for Corrupted { | 520 impl Graph for Corrupted { |
414 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> { | 521 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> { |
415 match rev { | 522 match rev { |
432 fn test_next_out_of_range() { | 539 fn test_next_out_of_range() { |
433 // inclusive=false looks up initrev's parents right away | 540 // inclusive=false looks up initrev's parents right away |
434 let mut iter = | 541 let mut iter = |
435 AncestorsIterator::new(Corrupted, vec![1], 0, false).unwrap(); | 542 AncestorsIterator::new(Corrupted, vec![1], 0, false).unwrap(); |
436 assert_eq!(iter.next(), Some(Err(GraphError::ParentOutOfRange(0)))); | 543 assert_eq!(iter.next(), Some(Err(GraphError::ParentOutOfRange(0)))); |
544 } | |
545 | |
546 #[test] | |
547 fn test_lazy_iter_contains() { | |
548 let mut lazy = | |
549 LazyAncestors::new(Stub, vec![11, 13], 0, false).unwrap(); | |
550 | |
551 let revs: Vec<Revision> = lazy.iter().map(|r| r.unwrap()).collect(); | |
552 // compare with iterator tests on the same initial revisions | |
553 assert_eq!(revs, vec![8, 7, 4, 3, 2, 1, 0]); | |
554 | |
555 // contains() results are correct, unaffected by the fact that | |
556 // we consumed entirely an iterator out of lazy | |
557 assert_eq!(lazy.contains(2), Ok(true)); | |
558 assert_eq!(lazy.contains(9), Ok(false)); | |
559 } | |
560 | |
561 #[test] | |
562 fn test_lazy_contains_iter() { | |
563 let mut lazy = | |
564 LazyAncestors::new(Stub, vec![11, 13], 0, false).unwrap(); // reminder: [8, 7, 4, 3, 2, 1, 0] | |
565 | |
566 assert_eq!(lazy.contains(2), Ok(true)); | |
567 assert_eq!(lazy.contains(6), Ok(false)); | |
568 | |
569 // after consumption of 2 by the inner iterator, results stay | |
570 // consistent | |
571 assert_eq!(lazy.contains(2), Ok(true)); | |
572 assert_eq!(lazy.contains(5), Ok(false)); | |
573 | |
574 // iter() still gives us a fresh iterator | |
575 let revs: Vec<Revision> = lazy.iter().map(|r| r.unwrap()).collect(); | |
576 assert_eq!(revs, vec![8, 7, 4, 3, 2, 1, 0]); | |
437 } | 577 } |
438 | 578 |
439 #[test] | 579 #[test] |
440 /// Test constructor, add/get bases | 580 /// Test constructor, add/get bases |
441 fn test_missing_bases() { | 581 fn test_missing_bases() { |