Show More
@@ -0,0 +1,12 | |||
|
1 | [package] | |
|
2 | name = "hgdirectffi" | |
|
3 | version = "0.1.0" | |
|
4 | authors = ["Georges Racinet <gracinet@anybox.fr>"] | |
|
5 | description = "Low level Python bindings for hg-core, going through existing C extensions" | |
|
6 | ||
|
7 | [dependencies] | |
|
8 | libc = "*" | |
|
9 | hg-core = { path = "../hg-core" } | |
|
10 | ||
|
11 | [lib] | |
|
12 | crate-type = ["staticlib"] |
@@ -0,0 +1,229 | |||
|
1 | // Copyright 2018 Georges Racinet <gracinet@anybox.fr> | |
|
2 | // | |
|
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. | |
|
5 | ||
|
6 | //! Bindings for CPython extension code | |
|
7 | //! | |
|
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 | |
|
10 | //! from the caller at instantiation. | |
|
11 | ||
|
12 | use hg::AncestorsIterator; | |
|
13 | use hg::{Graph, GraphError, Revision, NULL_REVISION}; | |
|
14 | use libc::{c_int, c_long, c_void, ssize_t}; | |
|
15 | use std::ptr::null_mut; | |
|
16 | use std::slice; | |
|
17 | ||
|
18 | type IndexPtr = *mut c_void; | |
|
19 | type IndexParentsFn = | |
|
20 | unsafe extern "C" fn(index: IndexPtr, rev: ssize_t, ps: *mut [c_int; 2], max_rev: c_int) | |
|
21 | -> c_int; | |
|
22 | ||
|
23 | /// A Graph backed up by objects and functions from revlog.c | |
|
24 | /// | |
|
25 | /// This implementation of the Graph trait, relies on (pointers to) | |
|
26 | /// - the C index object (`index` member) | |
|
27 | /// - the `index_get_parents()` function (`parents` member) | |
|
28 | pub struct Index { | |
|
29 | index: IndexPtr, | |
|
30 | parents: IndexParentsFn, | |
|
31 | } | |
|
32 | ||
|
33 | impl Index { | |
|
34 | pub fn new(index: IndexPtr, parents: IndexParentsFn) -> Self { | |
|
35 | Index { | |
|
36 | index: index, | |
|
37 | parents: parents, | |
|
38 | } | |
|
39 | } | |
|
40 | } | |
|
41 | ||
|
42 | impl Graph for Index { | |
|
43 | /// wrap a call to the C extern parents function | |
|
44 | fn parents(&self, rev: Revision) -> Result<(Revision, Revision), GraphError> { | |
|
45 | let mut res: [c_int; 2] = [0; 2]; | |
|
46 | let code = | |
|
47 | unsafe { (self.parents)(self.index, rev as ssize_t, &mut res as *mut [c_int; 2], rev) }; | |
|
48 | match code { | |
|
49 | 0 => Ok((res[0], res[1])), | |
|
50 | _ => Err(GraphError::ParentOutOfRange(rev)), | |
|
51 | } | |
|
52 | } | |
|
53 | } | |
|
54 | ||
|
55 | /// Wrapping of AncestorsIterator<Index> constructor, for C callers. | |
|
56 | /// | |
|
57 | /// Besides `initrevs`, `stoprev` and `inclusive`, that are converted | |
|
58 | /// we receive the index and the parents function as pointers | |
|
59 | #[no_mangle] | |
|
60 | pub extern "C" fn rustlazyancestors_init( | |
|
61 | index: IndexPtr, | |
|
62 | parents: IndexParentsFn, | |
|
63 | initrevslen: usize, | |
|
64 | initrevs: *mut c_long, | |
|
65 | stoprev: c_long, | |
|
66 | inclusive: c_int, | |
|
67 | ) -> *mut AncestorsIterator<Index> { | |
|
68 | unsafe { | |
|
69 | raw_init( | |
|
70 | Index::new(index, parents), | |
|
71 | initrevslen, | |
|
72 | initrevs, | |
|
73 | stoprev, | |
|
74 | inclusive, | |
|
75 | ) | |
|
76 | } | |
|
77 | } | |
|
78 | ||
|
79 | /// Testable (for any Graph) version of rustlazyancestors_init | |
|
80 | #[inline] | |
|
81 | unsafe fn raw_init<G: Graph>( | |
|
82 | graph: G, | |
|
83 | initrevslen: usize, | |
|
84 | initrevs: *mut c_long, | |
|
85 | stoprev: c_long, | |
|
86 | inclusive: c_int, | |
|
87 | ) -> *mut AncestorsIterator<G> { | |
|
88 | let inclb = match inclusive { | |
|
89 | 0 => false, | |
|
90 | 1 => true, | |
|
91 | _ => { | |
|
92 | return null_mut(); | |
|
93 | } | |
|
94 | }; | |
|
95 | ||
|
96 | let slice = slice::from_raw_parts(initrevs, initrevslen); | |
|
97 | ||
|
98 | Box::into_raw(Box::new(match AncestorsIterator::new( | |
|
99 | graph, | |
|
100 | slice.into_iter().map(|&r| r as Revision), | |
|
101 | stoprev as Revision, | |
|
102 | inclb, | |
|
103 | ) { | |
|
104 | Ok(it) => it, | |
|
105 | Err(_) => { | |
|
106 | return null_mut(); | |
|
107 | } | |
|
108 | })) | |
|
109 | } | |
|
110 | ||
|
111 | /// Deallocator to be called from C code | |
|
112 | #[no_mangle] | |
|
113 | pub extern "C" fn rustlazyancestors_drop(raw_iter: *mut AncestorsIterator<Index>) { | |
|
114 | raw_drop(raw_iter); | |
|
115 | } | |
|
116 | ||
|
117 | /// Testable (for any Graph) version of rustlazayancestors_drop | |
|
118 | #[inline] | |
|
119 | fn raw_drop<G: Graph>(raw_iter: *mut AncestorsIterator<G>) { | |
|
120 | unsafe { | |
|
121 | Box::from_raw(raw_iter); | |
|
122 | } | |
|
123 | } | |
|
124 | ||
|
125 | /// Iteration main method to be called from C code | |
|
126 | /// | |
|
127 | /// We convert the end of iteration into NULL_REVISION, | |
|
128 | /// it will be up to the C wrapper to convert that back into a Python end of | |
|
129 | /// iteration | |
|
130 | #[no_mangle] | |
|
131 | pub extern "C" fn rustlazyancestors_next(raw: *mut AncestorsIterator<Index>) -> c_long { | |
|
132 | raw_next(raw) | |
|
133 | } | |
|
134 | ||
|
135 | /// Testable (for any Graph) version of rustlazayancestors_next | |
|
136 | #[inline] | |
|
137 | fn raw_next<G: Graph>(raw: *mut AncestorsIterator<G>) -> c_long { | |
|
138 | let as_ref = unsafe { &mut *raw }; | |
|
139 | as_ref.next().unwrap_or(NULL_REVISION) as c_long | |
|
140 | } | |
|
141 | ||
|
142 | #[cfg(test)] | |
|
143 | mod tests { | |
|
144 | use super::*; | |
|
145 | use std::thread; | |
|
146 | ||
|
147 | #[derive(Clone, Debug)] | |
|
148 | struct Stub; | |
|
149 | ||
|
150 | impl Graph for Stub { | |
|
151 | fn parents(&self, r: Revision) -> Result<(Revision, Revision), GraphError> { | |
|
152 | match r { | |
|
153 | 25 => Err(GraphError::ParentOutOfRange(25)), | |
|
154 | _ => Ok((1, 2)), | |
|
155 | } | |
|
156 | } | |
|
157 | } | |
|
158 | ||
|
159 | /// Helper for test_init_next() | |
|
160 | fn stub_raw_init( | |
|
161 | initrevslen: usize, | |
|
162 | initrevs: usize, | |
|
163 | stoprev: c_long, | |
|
164 | inclusive: c_int, | |
|
165 | ) -> usize { | |
|
166 | unsafe { | |
|
167 | raw_init( | |
|
168 | Stub, | |
|
169 | initrevslen, | |
|
170 | initrevs as *mut c_long, | |
|
171 | stoprev, | |
|
172 | inclusive, | |
|
173 | ) as usize | |
|
174 | } | |
|
175 | } | |
|
176 | ||
|
177 | fn stub_raw_init_from_vec( | |
|
178 | mut initrevs: Vec<c_long>, | |
|
179 | stoprev: c_long, | |
|
180 | inclusive: c_int, | |
|
181 | ) -> *mut AncestorsIterator<Stub> { | |
|
182 | unsafe { | |
|
183 | raw_init( | |
|
184 | Stub, | |
|
185 | initrevs.len(), | |
|
186 | initrevs.as_mut_ptr(), | |
|
187 | stoprev, | |
|
188 | inclusive, | |
|
189 | ) | |
|
190 | } | |
|
191 | } | |
|
192 | ||
|
193 | #[test] | |
|
194 | // Test what happens when we init an Iterator as with the exposed C ABI | |
|
195 | // and try to use it afterwards | |
|
196 | // We spawn new threads, in order to make memory consistency harder | |
|
197 | // but this forces us to convert the pointers into shareable usizes. | |
|
198 | fn test_init_next() { | |
|
199 | let mut initrevs: Vec<c_long> = vec![11, 13]; | |
|
200 | let initrevs_len = initrevs.len(); | |
|
201 | let initrevs_ptr = initrevs.as_mut_ptr() as usize; | |
|
202 | let handler = thread::spawn(move || stub_raw_init(initrevs_len, initrevs_ptr, 0, 1)); | |
|
203 | let raw = handler.join().unwrap() as *mut AncestorsIterator<Stub>; | |
|
204 | ||
|
205 | assert_eq!(raw_next(raw), 13); | |
|
206 | assert_eq!(raw_next(raw), 11); | |
|
207 | assert_eq!(raw_next(raw), 2); | |
|
208 | assert_eq!(raw_next(raw), 1); | |
|
209 | assert_eq!(raw_next(raw), NULL_REVISION as c_long); | |
|
210 | raw_drop(raw); | |
|
211 | } | |
|
212 | ||
|
213 | #[test] | |
|
214 | fn test_init_wrong_bool() { | |
|
215 | assert_eq!(stub_raw_init_from_vec(vec![11, 13], 0, 2), null_mut()); | |
|
216 | } | |
|
217 | ||
|
218 | #[test] | |
|
219 | fn test_empty() { | |
|
220 | let raw = stub_raw_init_from_vec(vec![], 0, 1); | |
|
221 | assert_eq!(raw_next(raw), NULL_REVISION as c_long); | |
|
222 | raw_drop(raw); | |
|
223 | } | |
|
224 | ||
|
225 | #[test] | |
|
226 | fn test_init_err_out_of_range() { | |
|
227 | assert!(stub_raw_init_from_vec(vec![25], 0, 0).is_null()); | |
|
228 | } | |
|
229 | } |
@@ -0,0 +1,16 | |||
|
1 | // Copyright 2018 Georges Racinet <gracinet@anybox.fr> | |
|
2 | // | |
|
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. | |
|
5 | ||
|
6 | //! Bindings for CPython extension code | |
|
7 | //! | |
|
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 | |
|
10 | //! from the caller at instantiation. | |
|
11 | ||
|
12 | extern crate hg; | |
|
13 | extern crate libc; | |
|
14 | ||
|
15 | mod ancestors; | |
|
16 | pub use ancestors::{rustlazyancestors_drop, rustlazyancestors_init, rustlazyancestors_next}; |
@@ -30,6 +30,14 dependencies = [ | |||
|
30 | 30 | ] |
|
31 | 31 | |
|
32 | 32 | [[package]] |
|
33 | name = "hgdirectffi" | |
|
34 | version = "0.1.0" | |
|
35 | dependencies = [ | |
|
36 | "hg-core 0.1.0", | |
|
37 | "libc 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)", | |
|
38 | ] | |
|
39 | ||
|
40 | [[package]] | |
|
33 | 41 | name = "kernel32-sys" |
|
34 | 42 | version = "0.2.2" |
|
35 | 43 | source = "registry+https://github.com/rust-lang/crates.io-index" |
General Comments 0
You need to be logged in to leave comments.
Login now