##// END OF EJS Templates
rust: propagate error of index_get_parents() properly...
Yuya Nishihara -
r41244:443eb4bc default
parent child Browse files
Show More
@@ -1,272 +1,273 b''
1 // ancestors.rs
1 // ancestors.rs
2 //
2 //
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Rust versions of generic DAG ancestors algorithms for Mercurial
8 //! Rust versions of generic DAG ancestors algorithms for Mercurial
9
9
10 use super::{Graph, GraphError, Revision, NULL_REVISION};
10 use super::{Graph, GraphError, Revision, NULL_REVISION};
11 use std::collections::{BinaryHeap, HashSet};
11 use std::collections::{BinaryHeap, HashSet};
12
12
13 /// Iterator over the ancestors of a given list of revisions
13 /// Iterator over the ancestors of a given list of revisions
14 /// This is a generic type, defined and implemented for any Graph, so that
14 /// This is a generic type, defined and implemented for any Graph, so that
15 /// it's easy to
15 /// it's easy to
16 ///
16 ///
17 /// - unit test in pure Rust
17 /// - unit test in pure Rust
18 /// - bind to main Mercurial code, potentially in several ways and have these
18 /// - bind to main Mercurial code, potentially in several ways and have these
19 /// bindings evolve over time
19 /// bindings evolve over time
20 pub struct AncestorsIterator<G: Graph> {
20 pub struct AncestorsIterator<G: Graph> {
21 graph: G,
21 graph: G,
22 visit: BinaryHeap<Revision>,
22 visit: BinaryHeap<Revision>,
23 seen: HashSet<Revision>,
23 seen: HashSet<Revision>,
24 stoprev: Revision,
24 stoprev: Revision,
25 }
25 }
26
26
27 impl<G: Graph> AncestorsIterator<G> {
27 impl<G: Graph> AncestorsIterator<G> {
28 /// Constructor.
28 /// Constructor.
29 ///
29 ///
30 /// if `inclusive` is true, then the init revisions are emitted in
30 /// if `inclusive` is true, then the init revisions are emitted in
31 /// particular, otherwise iteration starts from their parents.
31 /// particular, otherwise iteration starts from their parents.
32 pub fn new<I>(
32 pub fn new<I>(
33 graph: G,
33 graph: G,
34 initrevs: I,
34 initrevs: I,
35 stoprev: Revision,
35 stoprev: Revision,
36 inclusive: bool,
36 inclusive: bool,
37 ) -> Result<Self, GraphError>
37 ) -> Result<Self, GraphError>
38 where
38 where
39 I: IntoIterator<Item = Revision>,
39 I: IntoIterator<Item = Revision>,
40 {
40 {
41 let filtered_initrevs = initrevs.into_iter().filter(|&r| r >= stoprev);
41 let filtered_initrevs = initrevs.into_iter().filter(|&r| r >= stoprev);
42 if inclusive {
42 if inclusive {
43 let visit: BinaryHeap<Revision> = filtered_initrevs.collect();
43 let visit: BinaryHeap<Revision> = filtered_initrevs.collect();
44 let seen = visit.iter().map(|&x| x).collect();
44 let seen = visit.iter().map(|&x| x).collect();
45 return Ok(AncestorsIterator {
45 return Ok(AncestorsIterator {
46 visit: visit,
46 visit: visit,
47 seen: seen,
47 seen: seen,
48 stoprev: stoprev,
48 stoprev: stoprev,
49 graph: graph,
49 graph: graph,
50 });
50 });
51 }
51 }
52 let mut this = AncestorsIterator {
52 let mut this = AncestorsIterator {
53 visit: BinaryHeap::new(),
53 visit: BinaryHeap::new(),
54 seen: HashSet::new(),
54 seen: HashSet::new(),
55 stoprev: stoprev,
55 stoprev: stoprev,
56 graph: graph,
56 graph: graph,
57 };
57 };
58 this.seen.insert(NULL_REVISION);
58 this.seen.insert(NULL_REVISION);
59 for rev in filtered_initrevs {
59 for rev in filtered_initrevs {
60 let parents = this.graph.parents(rev)?;
60 let parents = this.graph.parents(rev)?;
61 this.conditionally_push_rev(parents.0);
61 this.conditionally_push_rev(parents.0);
62 this.conditionally_push_rev(parents.1);
62 this.conditionally_push_rev(parents.1);
63 }
63 }
64 Ok(this)
64 Ok(this)
65 }
65 }
66
66
67 #[inline]
67 #[inline]
68 fn conditionally_push_rev(&mut self, rev: Revision) {
68 fn conditionally_push_rev(&mut self, rev: Revision) {
69 if self.stoprev <= rev && !self.seen.contains(&rev) {
69 if self.stoprev <= rev && !self.seen.contains(&rev) {
70 self.seen.insert(rev);
70 self.seen.insert(rev);
71 self.visit.push(rev);
71 self.visit.push(rev);
72 }
72 }
73 }
73 }
74
74
75 /// Consumes partially the iterator to tell if the given target
75 /// Consumes partially the iterator to tell if the given target
76 /// revision
76 /// revision
77 /// is in the ancestors it emits.
77 /// is in the ancestors it emits.
78 /// This is meant for iterators actually dedicated to that kind of
78 /// This is meant for iterators actually dedicated to that kind of
79 /// purpose
79 /// purpose
80 pub fn contains(&mut self, target: Revision) -> bool {
80 pub fn contains(&mut self, target: Revision) -> Result<bool, GraphError> {
81 if self.seen.contains(&target) && target != NULL_REVISION {
81 if self.seen.contains(&target) && target != NULL_REVISION {
82 return true;
82 return Ok(true);
83 }
83 }
84 for rev in self {
84 for item in self {
85 let rev = item?;
85 if rev == target {
86 if rev == target {
86 return true;
87 return Ok(true);
87 }
88 }
88 if rev < target {
89 if rev < target {
89 return false;
90 return Ok(false);
90 }
91 }
91 }
92 }
92 false
93 Ok(false)
93 }
94 }
94 }
95 }
95
96
96 /// Main implementation.
97 /// Main implementation.
97 ///
98 ///
98 /// The algorithm is the same as in `_lazyancestorsiter()` from `ancestors.py`
99 /// The algorithm is the same as in `_lazyancestorsiter()` from `ancestors.py`
99 /// with a few non crucial differences:
100 /// with a few non crucial differences:
100 ///
101 ///
101 /// - there's no filtering of invalid parent revisions. Actually, it should be
102 /// - there's no filtering of invalid parent revisions. Actually, it should be
102 /// consistent and more efficient to filter them from the end caller.
103 /// consistent and more efficient to filter them from the end caller.
103 /// - we don't have the optimization for adjacent revs
104 /// - we don't have the optimization for adjacent revs
104 /// (case where p1 == rev-1), because it amounts to update the first element
105 /// (case where p1 == rev-1), because it amounts to update the first element
105 /// of the heap without sifting, which Rust's BinaryHeap doesn't let us do.
106 /// of the heap without sifting, which Rust's BinaryHeap doesn't let us do.
106 /// - we save a few pushes by comparing with `stoprev` before pushing
107 /// - we save a few pushes by comparing with `stoprev` before pushing
107 ///
108 ///
108 /// Error treatment:
109 /// Error treatment:
109 /// We swallow the possible GraphError of conditionally_push_parents() to
110 /// We swallow the possible GraphError of conditionally_push_parents() to
110 /// respect the Iterator trait in a simple manner: never emitting parents
111 /// respect the Iterator trait in a simple manner: never emitting parents
111 /// for the returned revision. We finds this good enough for now, because:
112 /// for the returned revision. We finds this good enough for now, because:
112 ///
113 ///
113 /// - there's a good chance that invalid revisionss are fed from the start,
114 /// - there's a good chance that invalid revisionss are fed from the start,
114 /// and `new()` doesn't swallow the error result.
115 /// and `new()` doesn't swallow the error result.
115 /// - this is probably what the Python implementation produces anyway, due
116 /// - this is probably what the Python implementation produces anyway, due
116 /// to filtering at each step, and Python code is currently the only
117 /// to filtering at each step, and Python code is currently the only
117 /// concrete caller we target, so we shouldn't need a finer error treatment
118 /// concrete caller we target, so we shouldn't need a finer error treatment
118 /// for the time being.
119 /// for the time being.
119 impl<G: Graph> Iterator for AncestorsIterator<G> {
120 impl<G: Graph> Iterator for AncestorsIterator<G> {
120 type Item = Revision;
121 type Item = Result<Revision, GraphError>;
121
122
122 fn next(&mut self) -> Option<Revision> {
123 fn next(&mut self) -> Option<Self::Item> {
123 let current = match self.visit.peek() {
124 let current = match self.visit.peek() {
124 None => {
125 None => {
125 return None;
126 return None;
126 }
127 }
127 Some(c) => *c,
128 Some(c) => *c,
128 };
129 };
129 let (p1, p2) = self
130 let (p1, p2) = match self.graph.parents(current) {
130 .graph
131 Ok(ps) => ps,
131 .parents(current)
132 Err(e) => return Some(Err(e)),
132 .unwrap_or((NULL_REVISION, NULL_REVISION));
133 };
133 if p1 < self.stoprev || self.seen.contains(&p1) {
134 if p1 < self.stoprev || self.seen.contains(&p1) {
134 self.visit.pop();
135 self.visit.pop();
135 } else {
136 } else {
136 *(self.visit.peek_mut().unwrap()) = p1;
137 *(self.visit.peek_mut().unwrap()) = p1;
137 self.seen.insert(p1);
138 self.seen.insert(p1);
138 };
139 };
139
140
140 self.conditionally_push_rev(p2);
141 self.conditionally_push_rev(p2);
141 Some(current)
142 Some(Ok(current))
142 }
143 }
143 }
144 }
144
145
145 #[cfg(test)]
146 #[cfg(test)]
146 mod tests {
147 mod tests {
147
148
148 use super::*;
149 use super::*;
149
150
150 #[derive(Clone, Debug)]
151 #[derive(Clone, Debug)]
151 struct Stub;
152 struct Stub;
152
153
153 /// This is the same as the dict from test-ancestors.py
154 /// This is the same as the dict from test-ancestors.py
154 impl Graph for Stub {
155 impl Graph for Stub {
155 fn parents(
156 fn parents(
156 &self,
157 &self,
157 rev: Revision,
158 rev: Revision,
158 ) -> Result<(Revision, Revision), GraphError> {
159 ) -> Result<(Revision, Revision), GraphError> {
159 match rev {
160 match rev {
160 0 => Ok((-1, -1)),
161 0 => Ok((-1, -1)),
161 1 => Ok((0, -1)),
162 1 => Ok((0, -1)),
162 2 => Ok((1, -1)),
163 2 => Ok((1, -1)),
163 3 => Ok((1, -1)),
164 3 => Ok((1, -1)),
164 4 => Ok((2, -1)),
165 4 => Ok((2, -1)),
165 5 => Ok((4, -1)),
166 5 => Ok((4, -1)),
166 6 => Ok((4, -1)),
167 6 => Ok((4, -1)),
167 7 => Ok((4, -1)),
168 7 => Ok((4, -1)),
168 8 => Ok((-1, -1)),
169 8 => Ok((-1, -1)),
169 9 => Ok((6, 7)),
170 9 => Ok((6, 7)),
170 10 => Ok((5, -1)),
171 10 => Ok((5, -1)),
171 11 => Ok((3, 7)),
172 11 => Ok((3, 7)),
172 12 => Ok((9, -1)),
173 12 => Ok((9, -1)),
173 13 => Ok((8, -1)),
174 13 => Ok((8, -1)),
174 r => Err(GraphError::ParentOutOfRange(r)),
175 r => Err(GraphError::ParentOutOfRange(r)),
175 }
176 }
176 }
177 }
177 }
178 }
178
179
179 fn list_ancestors<G: Graph>(
180 fn list_ancestors<G: Graph>(
180 graph: G,
181 graph: G,
181 initrevs: Vec<Revision>,
182 initrevs: Vec<Revision>,
182 stoprev: Revision,
183 stoprev: Revision,
183 inclusive: bool,
184 inclusive: bool,
184 ) -> Vec<Revision> {
185 ) -> Vec<Revision> {
185 AncestorsIterator::new(graph, initrevs, stoprev, inclusive)
186 AncestorsIterator::new(graph, initrevs, stoprev, inclusive)
186 .unwrap()
187 .unwrap()
187 .collect()
188 .collect()
188 }
189 }
189
190
190 #[test]
191 #[test]
191 /// Same tests as test-ancestor.py, without membership
192 /// Same tests as test-ancestor.py, without membership
192 /// (see also test-ancestor.py.out)
193 /// (see also test-ancestor.py.out)
193 fn test_list_ancestor() {
194 fn test_list_ancestor() {
194 assert_eq!(list_ancestors(Stub, vec![], 0, false), vec![]);
195 assert_eq!(list_ancestors(Stub, vec![], 0, false), vec![]);
195 assert_eq!(
196 assert_eq!(
196 list_ancestors(Stub, vec![11, 13], 0, false),
197 list_ancestors(Stub, vec![11, 13], 0, false),
197 vec![8, 7, 4, 3, 2, 1, 0]
198 vec![8, 7, 4, 3, 2, 1, 0]
198 );
199 );
199 assert_eq!(list_ancestors(Stub, vec![1, 3], 0, false), vec![1, 0]);
200 assert_eq!(list_ancestors(Stub, vec![1, 3], 0, false), vec![1, 0]);
200 assert_eq!(
201 assert_eq!(
201 list_ancestors(Stub, vec![11, 13], 0, true),
202 list_ancestors(Stub, vec![11, 13], 0, true),
202 vec![13, 11, 8, 7, 4, 3, 2, 1, 0]
203 vec![13, 11, 8, 7, 4, 3, 2, 1, 0]
203 );
204 );
204 assert_eq!(list_ancestors(Stub, vec![11, 13], 6, false), vec![8, 7]);
205 assert_eq!(list_ancestors(Stub, vec![11, 13], 6, false), vec![8, 7]);
205 assert_eq!(
206 assert_eq!(
206 list_ancestors(Stub, vec![11, 13], 6, true),
207 list_ancestors(Stub, vec![11, 13], 6, true),
207 vec![13, 11, 8, 7]
208 vec![13, 11, 8, 7]
208 );
209 );
209 assert_eq!(list_ancestors(Stub, vec![11, 13], 11, true), vec![13, 11]);
210 assert_eq!(list_ancestors(Stub, vec![11, 13], 11, true), vec![13, 11]);
210 assert_eq!(list_ancestors(Stub, vec![11, 13], 12, true), vec![13]);
211 assert_eq!(list_ancestors(Stub, vec![11, 13], 12, true), vec![13]);
211 assert_eq!(
212 assert_eq!(
212 list_ancestors(Stub, vec![10, 1], 0, true),
213 list_ancestors(Stub, vec![10, 1], 0, true),
213 vec![10, 5, 4, 2, 1, 0]
214 vec![10, 5, 4, 2, 1, 0]
214 );
215 );
215 }
216 }
216
217
217 #[test]
218 #[test]
218 /// Corner case that's not directly in test-ancestors.py, but
219 /// Corner case that's not directly in test-ancestors.py, but
219 /// that happens quite often, as demonstrated by running the whole
220 /// that happens quite often, as demonstrated by running the whole
220 /// suite.
221 /// suite.
221 /// For instance, run tests/test-obsolete-checkheads.t
222 /// For instance, run tests/test-obsolete-checkheads.t
222 fn test_nullrev_input() {
223 fn test_nullrev_input() {
223 let mut iter =
224 let mut iter =
224 AncestorsIterator::new(Stub, vec![-1], 0, false).unwrap();
225 AncestorsIterator::new(Stub, vec![-1], 0, false).unwrap();
225 assert_eq!(iter.next(), None)
226 assert_eq!(iter.next(), None)
226 }
227 }
227
228
228 #[test]
229 #[test]
229 fn test_contains() {
230 fn test_contains() {
230 let mut lazy =
231 let mut lazy =
231 AncestorsIterator::new(Stub, vec![10, 1], 0, true).unwrap();
232 AncestorsIterator::new(Stub, vec![10, 1], 0, true).unwrap();
232 assert!(lazy.contains(1));
233 assert!(lazy.contains(1));
233 assert!(!lazy.contains(3));
234 assert!(!lazy.contains(3));
234
235
235 let mut lazy =
236 let mut lazy =
236 AncestorsIterator::new(Stub, vec![0], 0, false).unwrap();
237 AncestorsIterator::new(Stub, vec![0], 0, false).unwrap();
237 assert!(!lazy.contains(NULL_REVISION));
238 assert!(!lazy.contains(NULL_REVISION));
238 }
239 }
239
240
240 /// A corrupted Graph, supporting error handling tests
241 /// A corrupted Graph, supporting error handling tests
241 struct Corrupted;
242 struct Corrupted;
242
243
243 impl Graph for Corrupted {
244 impl Graph for Corrupted {
244 fn parents(
245 fn parents(
245 &self,
246 &self,
246 rev: Revision,
247 rev: Revision,
247 ) -> Result<(Revision, Revision), GraphError> {
248 ) -> Result<(Revision, Revision), GraphError> {
248 match rev {
249 match rev {
249 1 => Ok((0, -1)),
250 1 => Ok((0, -1)),
250 r => Err(GraphError::ParentOutOfRange(r)),
251 r => Err(GraphError::ParentOutOfRange(r)),
251 }
252 }
252 }
253 }
253 }
254 }
254
255
255 #[test]
256 #[test]
256 fn test_initrev_out_of_range() {
257 fn test_initrev_out_of_range() {
257 // inclusive=false looks up initrev's parents right away
258 // inclusive=false looks up initrev's parents right away
258 match AncestorsIterator::new(Stub, vec![25], 0, false) {
259 match AncestorsIterator::new(Stub, vec![25], 0, false) {
259 Ok(_) => panic!("Should have been ParentOutOfRange"),
260 Ok(_) => panic!("Should have been ParentOutOfRange"),
260 Err(e) => assert_eq!(e, GraphError::ParentOutOfRange(25)),
261 Err(e) => assert_eq!(e, GraphError::ParentOutOfRange(25)),
261 }
262 }
262 }
263 }
263
264
264 #[test]
265 #[test]
265 fn test_next_out_of_range() {
266 fn test_next_out_of_range() {
266 // inclusive=false looks up initrev's parents right away
267 // inclusive=false looks up initrev's parents right away
267 let mut iter =
268 let mut iter =
268 AncestorsIterator::new(Corrupted, vec![1], 0, false).unwrap();
269 AncestorsIterator::new(Corrupted, vec![1], 0, false).unwrap();
269 assert_eq!(iter.next(), Some(0));
270 assert_eq!(iter.next(), Some(0));
270 assert_eq!(iter.next(), None);
271 assert_eq!(iter.next(), None);
271 }
272 }
272 }
273 }
@@ -1,267 +1,271 b''
1 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
1 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
2 //
2 //
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
5
6 //! Bindings for CPython extension code
6 //! Bindings for CPython extension code
7 //!
7 //!
8 //! This exposes methods to build and use a `rustlazyancestors` iterator
8 //! This exposes methods to build and use a `rustlazyancestors` iterator
9 //! from C code, using an index and its parents function that are passed
9 //! from C code, using an index and its parents function that are passed
10 //! from the caller at instantiation.
10 //! from the caller at instantiation.
11
11
12 use hg::AncestorsIterator;
12 use hg::AncestorsIterator;
13 use hg::{Graph, GraphError, Revision, NULL_REVISION};
13 use hg::{Graph, GraphError, Revision, NULL_REVISION};
14 use libc::{c_int, c_long, c_void, ssize_t};
14 use libc::{c_int, c_long, c_void, ssize_t};
15 use std::ptr::null_mut;
15 use std::ptr::null_mut;
16 use std::slice;
16 use std::slice;
17
17
18 type IndexPtr = *mut c_void;
18 type IndexPtr = *mut c_void;
19
19
20 extern "C" {
20 extern "C" {
21 fn HgRevlogIndex_GetParents(
21 fn HgRevlogIndex_GetParents(
22 op: IndexPtr,
22 op: IndexPtr,
23 rev: c_int,
23 rev: c_int,
24 parents: *mut [c_int; 2],
24 parents: *mut [c_int; 2],
25 ) -> c_int;
25 ) -> c_int;
26 }
26 }
27
27
28 /// A Graph backed up by objects and functions from revlog.c
28 /// A Graph backed up by objects and functions from revlog.c
29 ///
29 ///
30 /// This implementation of the Graph trait, relies on (pointers to)
30 /// This implementation of the Graph trait, relies on (pointers to)
31 /// - the C index object (`index` member)
31 /// - the C index object (`index` member)
32 /// - the `index_get_parents()` function (`parents` member)
32 /// - the `index_get_parents()` function (`parents` member)
33 pub struct Index {
33 pub struct Index {
34 index: IndexPtr,
34 index: IndexPtr,
35 }
35 }
36
36
37 impl Index {
37 impl Index {
38 pub fn new(index: IndexPtr) -> Self {
38 pub fn new(index: IndexPtr) -> Self {
39 Index {
39 Index {
40 index: index,
40 index: index,
41 }
41 }
42 }
42 }
43 }
43 }
44
44
45 impl Graph for Index {
45 impl Graph for Index {
46 /// wrap a call to the C extern parents function
46 /// wrap a call to the C extern parents function
47 fn parents(&self, rev: Revision) -> Result<(Revision, Revision), GraphError> {
47 fn parents(&self, rev: Revision) -> Result<(Revision, Revision), GraphError> {
48 let mut res: [c_int; 2] = [0; 2];
48 let mut res: [c_int; 2] = [0; 2];
49 let code =
49 let code =
50 unsafe { HgRevlogIndex_GetParents(self.index, rev, &mut res as *mut [c_int; 2]) };
50 unsafe { HgRevlogIndex_GetParents(self.index, rev, &mut res as *mut [c_int; 2]) };
51 match code {
51 match code {
52 0 => Ok((res[0], res[1])),
52 0 => Ok((res[0], res[1])),
53 _ => Err(GraphError::ParentOutOfRange(rev)),
53 _ => Err(GraphError::ParentOutOfRange(rev)),
54 }
54 }
55 }
55 }
56 }
56 }
57
57
58 /// Wrapping of AncestorsIterator<Index> constructor, for C callers.
58 /// Wrapping of AncestorsIterator<Index> constructor, for C callers.
59 ///
59 ///
60 /// Besides `initrevs`, `stoprev` and `inclusive`, that are converted
60 /// Besides `initrevs`, `stoprev` and `inclusive`, that are converted
61 /// we receive the index and the parents function as pointers
61 /// we receive the index and the parents function as pointers
62 #[no_mangle]
62 #[no_mangle]
63 pub extern "C" fn rustlazyancestors_init(
63 pub extern "C" fn rustlazyancestors_init(
64 index: IndexPtr,
64 index: IndexPtr,
65 initrevslen: ssize_t,
65 initrevslen: ssize_t,
66 initrevs: *mut c_long,
66 initrevs: *mut c_long,
67 stoprev: c_long,
67 stoprev: c_long,
68 inclusive: c_int,
68 inclusive: c_int,
69 ) -> *mut AncestorsIterator<Index> {
69 ) -> *mut AncestorsIterator<Index> {
70 assert!(initrevslen >= 0);
70 assert!(initrevslen >= 0);
71 unsafe {
71 unsafe {
72 raw_init(
72 raw_init(
73 Index::new(index),
73 Index::new(index),
74 initrevslen as usize,
74 initrevslen as usize,
75 initrevs,
75 initrevs,
76 stoprev,
76 stoprev,
77 inclusive,
77 inclusive,
78 )
78 )
79 }
79 }
80 }
80 }
81
81
82 /// Testable (for any Graph) version of rustlazyancestors_init
82 /// Testable (for any Graph) version of rustlazyancestors_init
83 #[inline]
83 #[inline]
84 unsafe fn raw_init<G: Graph>(
84 unsafe fn raw_init<G: Graph>(
85 graph: G,
85 graph: G,
86 initrevslen: usize,
86 initrevslen: usize,
87 initrevs: *mut c_long,
87 initrevs: *mut c_long,
88 stoprev: c_long,
88 stoprev: c_long,
89 inclusive: c_int,
89 inclusive: c_int,
90 ) -> *mut AncestorsIterator<G> {
90 ) -> *mut AncestorsIterator<G> {
91 let inclb = match inclusive {
91 let inclb = match inclusive {
92 0 => false,
92 0 => false,
93 1 => true,
93 1 => true,
94 _ => {
94 _ => {
95 return null_mut();
95 return null_mut();
96 }
96 }
97 };
97 };
98
98
99 let slice = slice::from_raw_parts(initrevs, initrevslen);
99 let slice = slice::from_raw_parts(initrevs, initrevslen);
100
100
101 Box::into_raw(Box::new(match AncestorsIterator::new(
101 Box::into_raw(Box::new(match AncestorsIterator::new(
102 graph,
102 graph,
103 slice.into_iter().map(|&r| r as Revision),
103 slice.into_iter().map(|&r| r as Revision),
104 stoprev as Revision,
104 stoprev as Revision,
105 inclb,
105 inclb,
106 ) {
106 ) {
107 Ok(it) => it,
107 Ok(it) => it,
108 Err(_) => {
108 Err(_) => {
109 return null_mut();
109 return null_mut();
110 }
110 }
111 }))
111 }))
112 }
112 }
113
113
114 /// Deallocator to be called from C code
114 /// Deallocator to be called from C code
115 #[no_mangle]
115 #[no_mangle]
116 pub extern "C" fn rustlazyancestors_drop(raw_iter: *mut AncestorsIterator<Index>) {
116 pub extern "C" fn rustlazyancestors_drop(raw_iter: *mut AncestorsIterator<Index>) {
117 raw_drop(raw_iter);
117 raw_drop(raw_iter);
118 }
118 }
119
119
120 /// Testable (for any Graph) version of rustlazayancestors_drop
120 /// Testable (for any Graph) version of rustlazayancestors_drop
121 #[inline]
121 #[inline]
122 fn raw_drop<G: Graph>(raw_iter: *mut AncestorsIterator<G>) {
122 fn raw_drop<G: Graph>(raw_iter: *mut AncestorsIterator<G>) {
123 unsafe {
123 unsafe {
124 Box::from_raw(raw_iter);
124 Box::from_raw(raw_iter);
125 }
125 }
126 }
126 }
127
127
128 /// Iteration main method to be called from C code
128 /// Iteration main method to be called from C code
129 ///
129 ///
130 /// We convert the end of iteration into NULL_REVISION,
130 /// We convert the end of iteration into NULL_REVISION,
131 /// it will be up to the C wrapper to convert that back into a Python end of
131 /// it will be up to the C wrapper to convert that back into a Python end of
132 /// iteration
132 /// iteration
133 #[no_mangle]
133 #[no_mangle]
134 pub extern "C" fn rustlazyancestors_next(raw: *mut AncestorsIterator<Index>) -> c_long {
134 pub extern "C" fn rustlazyancestors_next(raw: *mut AncestorsIterator<Index>) -> c_long {
135 raw_next(raw)
135 raw_next(raw)
136 }
136 }
137
137
138 /// Testable (for any Graph) version of rustlazayancestors_next
138 /// Testable (for any Graph) version of rustlazayancestors_next
139 #[inline]
139 #[inline]
140 fn raw_next<G: Graph>(raw: *mut AncestorsIterator<G>) -> c_long {
140 fn raw_next<G: Graph>(raw: *mut AncestorsIterator<G>) -> c_long {
141 let as_ref = unsafe { &mut *raw };
141 let as_ref = unsafe { &mut *raw };
142 as_ref.next().unwrap_or(NULL_REVISION) as c_long
142 let rev = match as_ref.next() {
143 Some(Ok(rev)) => rev,
144 Some(Err(_)) | None => NULL_REVISION,
145 };
146 rev as c_long
143 }
147 }
144
148
145 #[no_mangle]
149 #[no_mangle]
146 pub extern "C" fn rustlazyancestors_contains(
150 pub extern "C" fn rustlazyancestors_contains(
147 raw: *mut AncestorsIterator<Index>,
151 raw: *mut AncestorsIterator<Index>,
148 target: c_long,
152 target: c_long,
149 ) -> c_int {
153 ) -> c_int {
150 raw_contains(raw, target)
154 raw_contains(raw, target)
151 }
155 }
152
156
153 /// Testable (for any Graph) version of rustlazayancestors_next
157 /// Testable (for any Graph) version of rustlazayancestors_next
154 #[inline]
158 #[inline]
155 fn raw_contains<G: Graph>(
159 fn raw_contains<G: Graph>(
156 raw: *mut AncestorsIterator<G>,
160 raw: *mut AncestorsIterator<G>,
157 target: c_long,
161 target: c_long,
158 ) -> c_int {
162 ) -> c_int {
159 let as_ref = unsafe { &mut *raw };
163 let as_ref = unsafe { &mut *raw };
160 if as_ref.contains(target as Revision) {
164 match as_ref.contains(target as Revision) {
161 return 1;
165 Ok(r) => r as c_int,
166 Err(_) => -1,
162 }
167 }
163 0
164 }
168 }
165
169
166 #[cfg(test)]
170 #[cfg(test)]
167 mod tests {
171 mod tests {
168 use super::*;
172 use super::*;
169 use std::thread;
173 use std::thread;
170
174
171 #[derive(Clone, Debug)]
175 #[derive(Clone, Debug)]
172 struct Stub;
176 struct Stub;
173
177
174 impl Graph for Stub {
178 impl Graph for Stub {
175 fn parents(&self, r: Revision) -> Result<(Revision, Revision), GraphError> {
179 fn parents(&self, r: Revision) -> Result<(Revision, Revision), GraphError> {
176 match r {
180 match r {
177 25 => Err(GraphError::ParentOutOfRange(25)),
181 25 => Err(GraphError::ParentOutOfRange(25)),
178 _ => Ok((1, 2)),
182 _ => Ok((1, 2)),
179 }
183 }
180 }
184 }
181 }
185 }
182
186
183 /// Helper for test_init_next()
187 /// Helper for test_init_next()
184 fn stub_raw_init(
188 fn stub_raw_init(
185 initrevslen: usize,
189 initrevslen: usize,
186 initrevs: usize,
190 initrevs: usize,
187 stoprev: c_long,
191 stoprev: c_long,
188 inclusive: c_int,
192 inclusive: c_int,
189 ) -> usize {
193 ) -> usize {
190 unsafe {
194 unsafe {
191 raw_init(
195 raw_init(
192 Stub,
196 Stub,
193 initrevslen,
197 initrevslen,
194 initrevs as *mut c_long,
198 initrevs as *mut c_long,
195 stoprev,
199 stoprev,
196 inclusive,
200 inclusive,
197 ) as usize
201 ) as usize
198 }
202 }
199 }
203 }
200
204
201 fn stub_raw_init_from_vec(
205 fn stub_raw_init_from_vec(
202 mut initrevs: Vec<c_long>,
206 mut initrevs: Vec<c_long>,
203 stoprev: c_long,
207 stoprev: c_long,
204 inclusive: c_int,
208 inclusive: c_int,
205 ) -> *mut AncestorsIterator<Stub> {
209 ) -> *mut AncestorsIterator<Stub> {
206 unsafe {
210 unsafe {
207 raw_init(
211 raw_init(
208 Stub,
212 Stub,
209 initrevs.len(),
213 initrevs.len(),
210 initrevs.as_mut_ptr(),
214 initrevs.as_mut_ptr(),
211 stoprev,
215 stoprev,
212 inclusive,
216 inclusive,
213 )
217 )
214 }
218 }
215 }
219 }
216
220
217 #[test]
221 #[test]
218 // Test what happens when we init an Iterator as with the exposed C ABI
222 // Test what happens when we init an Iterator as with the exposed C ABI
219 // and try to use it afterwards
223 // and try to use it afterwards
220 // We spawn new threads, in order to make memory consistency harder
224 // We spawn new threads, in order to make memory consistency harder
221 // but this forces us to convert the pointers into shareable usizes.
225 // but this forces us to convert the pointers into shareable usizes.
222 fn test_init_next() {
226 fn test_init_next() {
223 let mut initrevs: Vec<c_long> = vec![11, 13];
227 let mut initrevs: Vec<c_long> = vec![11, 13];
224 let initrevs_len = initrevs.len();
228 let initrevs_len = initrevs.len();
225 let initrevs_ptr = initrevs.as_mut_ptr() as usize;
229 let initrevs_ptr = initrevs.as_mut_ptr() as usize;
226 let handler = thread::spawn(move || stub_raw_init(initrevs_len, initrevs_ptr, 0, 1));
230 let handler = thread::spawn(move || stub_raw_init(initrevs_len, initrevs_ptr, 0, 1));
227 let raw = handler.join().unwrap() as *mut AncestorsIterator<Stub>;
231 let raw = handler.join().unwrap() as *mut AncestorsIterator<Stub>;
228
232
229 assert_eq!(raw_next(raw), 13);
233 assert_eq!(raw_next(raw), 13);
230 assert_eq!(raw_next(raw), 11);
234 assert_eq!(raw_next(raw), 11);
231 assert_eq!(raw_next(raw), 2);
235 assert_eq!(raw_next(raw), 2);
232 assert_eq!(raw_next(raw), 1);
236 assert_eq!(raw_next(raw), 1);
233 assert_eq!(raw_next(raw), NULL_REVISION as c_long);
237 assert_eq!(raw_next(raw), NULL_REVISION as c_long);
234 raw_drop(raw);
238 raw_drop(raw);
235 }
239 }
236
240
237 #[test]
241 #[test]
238 fn test_init_wrong_bool() {
242 fn test_init_wrong_bool() {
239 assert_eq!(stub_raw_init_from_vec(vec![11, 13], 0, 2), null_mut());
243 assert_eq!(stub_raw_init_from_vec(vec![11, 13], 0, 2), null_mut());
240 }
244 }
241
245
242 #[test]
246 #[test]
243 fn test_empty() {
247 fn test_empty() {
244 let raw = stub_raw_init_from_vec(vec![], 0, 1);
248 let raw = stub_raw_init_from_vec(vec![], 0, 1);
245 assert_eq!(raw_next(raw), NULL_REVISION as c_long);
249 assert_eq!(raw_next(raw), NULL_REVISION as c_long);
246 raw_drop(raw);
250 raw_drop(raw);
247 }
251 }
248
252
249 #[test]
253 #[test]
250 fn test_init_err_out_of_range() {
254 fn test_init_err_out_of_range() {
251 assert!(stub_raw_init_from_vec(vec![25], 0, 0).is_null());
255 assert!(stub_raw_init_from_vec(vec![25], 0, 0).is_null());
252 }
256 }
253
257
254 #[test]
258 #[test]
255 fn test_contains() {
259 fn test_contains() {
256 let raw = stub_raw_init_from_vec(vec![5, 6], 0, 1);
260 let raw = stub_raw_init_from_vec(vec![5, 6], 0, 1);
257 assert_eq!(raw_contains(raw, 5), 1);
261 assert_eq!(raw_contains(raw, 5), 1);
258 assert_eq!(raw_contains(raw, 2), 1);
262 assert_eq!(raw_contains(raw, 2), 1);
259 }
263 }
260
264
261 #[test]
265 #[test]
262 fn test_contains_exclusive() {
266 fn test_contains_exclusive() {
263 let raw = stub_raw_init_from_vec(vec![5, 6], 0, 0);
267 let raw = stub_raw_init_from_vec(vec![5, 6], 0, 0);
264 assert_eq!(raw_contains(raw, 5), 0);
268 assert_eq!(raw_contains(raw, 5), 0);
265 assert_eq!(raw_contains(raw, 2), 1);
269 assert_eq!(raw_contains(raw, 2), 1);
266 }
270 }
267 }
271 }
General Comments 0
You need to be logged in to leave comments. Login now