Show More
@@ -25,6 +25,15 b' pub struct AncestorsIterator<G: Graph> {' | |||||
25 | stoprev: Revision, |
|
25 | stoprev: Revision, | |
26 | } |
|
26 | } | |
27 |
|
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, | |||
|
35 | } | |||
|
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>, | |
@@ -98,9 +107,31 b' impl<G: Graph> AncestorsIterator<G> {' | |||||
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: | |
@@ -137,6 +168,49 b' impl<G: Graph> Iterator for AncestorsIte' | |||||
137 | } |
|
168 | } | |
138 | } |
|
169 | } | |
139 |
|
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() | |||
|
211 | } | |||
|
212 | } | |||
|
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 { | |
142 | let mut bases: HashSet<Revision> = bases.into_iter().collect(); |
|
216 | let mut bases: HashSet<Revision> = bases.into_iter().collect(); | |
@@ -407,7 +481,40 b' mod tests {' | |||||
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 { | |
@@ -437,6 +544,39 b' mod tests {' | |||||
437 | } |
|
544 | } | |
438 |
|
545 | |||
439 | #[test] |
|
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]); | |||
|
577 | } | |||
|
578 | ||||
|
579 | #[test] | |||
440 | /// Test constructor, add/get bases |
|
580 | /// Test constructor, add/get bases | |
441 | fn test_missing_bases() { |
|
581 | fn test_missing_bases() { | |
442 | let mut missing_ancestors = |
|
582 | let mut missing_ancestors = |
@@ -3,7 +3,7 b'' | |||||
3 | // This software may be used and distributed according to the terms of the |
|
3 | // This software may be used and distributed according to the terms of the | |
4 | // GNU General Public License version 2 or any later version. |
|
4 | // GNU General Public License version 2 or any later version. | |
5 | mod ancestors; |
|
5 | mod ancestors; | |
6 | pub use ancestors::{AncestorsIterator, MissingAncestors}; |
|
6 | pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors}; | |
7 |
|
7 | |||
8 | /// Mercurial revision numbers |
|
8 | /// Mercurial revision numbers | |
9 | /// |
|
9 | /// |
@@ -15,7 +15,7 b' extern crate python27_sys as python_sys;' | |||||
15 | extern crate python3_sys as python_sys; |
|
15 | extern crate python3_sys as python_sys; | |
16 |
|
16 | |||
17 | use self::python_sys::PyCapsule_Import; |
|
17 | use self::python_sys::PyCapsule_Import; | |
18 | use cpython::{PyErr, PyObject, PyResult, Python}; |
|
18 | use cpython::{PyClone, PyErr, PyObject, PyResult, Python}; | |
19 | use hg::{Graph, GraphError, Revision}; |
|
19 | use hg::{Graph, GraphError, Revision}; | |
20 | use libc::c_int; |
|
20 | use libc::c_int; | |
21 | use std::ffi::CStr; |
|
21 | use std::ffi::CStr; | |
@@ -59,7 +59,6 b' type IndexParentsFn = unsafe extern "C" ' | |||||
59 | /// the core API, that would be tied to `GILGuard` / `Python<'p>` |
|
59 | /// the core API, that would be tied to `GILGuard` / `Python<'p>` | |
60 | /// in the case of the `cpython` crate bindings yet could leave room for other |
|
60 | /// in the case of the `cpython` crate bindings yet could leave room for other | |
61 | /// mechanisms in other contexts. |
|
61 | /// mechanisms in other contexts. | |
62 |
|
||||
63 | pub struct Index { |
|
62 | pub struct Index { | |
64 | index: PyObject, |
|
63 | index: PyObject, | |
65 | parents: IndexParentsFn, |
|
64 | parents: IndexParentsFn, | |
@@ -74,6 +73,16 b' impl Index {' | |||||
74 | } |
|
73 | } | |
75 | } |
|
74 | } | |
76 |
|
75 | |||
|
76 | impl Clone for Index { | |||
|
77 | fn clone(&self) -> Self { | |||
|
78 | let guard = Python::acquire_gil(); | |||
|
79 | Index { | |||
|
80 | index: self.index.clone_ref(guard.python()), | |||
|
81 | parents: self.parents.clone(), | |||
|
82 | } | |||
|
83 | } | |||
|
84 | } | |||
|
85 | ||||
77 | impl Graph for Index { |
|
86 | impl Graph for Index { | |
78 | /// wrap a call to the C extern parents function |
|
87 | /// wrap a call to the C extern parents function | |
79 | fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> { |
|
88 | fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> { |
General Comments 0
You need to be logged in to leave comments.
Login now