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() {