##// END OF EJS Templates
rust: iterator bindings to C code...
Georges Racinet -
r40308:a36c5e23 default
parent child Browse files
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"
@@ -1,3 +1,3
1 1 [workspace]
2 members = ["hgcli", "hg-core"]
2 members = ["hgcli", "hg-core", "hg-direct-ffi"]
3 3 exclude = ["chg"]
General Comments 0
You need to be logged in to leave comments. Login now