Show More
@@ -1,183 +1,201 b'' | |||
|
1 | 1 | // cindex.rs |
|
2 | 2 | // |
|
3 | 3 | // Copyright 2018 Georges Racinet <gracinet@anybox.fr> |
|
4 | 4 | // |
|
5 | 5 | // This software may be used and distributed according to the terms of the |
|
6 | 6 | // GNU General Public License version 2 or any later version. |
|
7 | 7 | |
|
8 | 8 | //! Bindings to use the Index defined by the parsers C extension |
|
9 | 9 | //! |
|
10 | 10 | //! Ideally, we should use an Index entirely implemented in Rust, |
|
11 | 11 | //! but this will take some time to get there. |
|
12 | 12 | |
|
13 | 13 | use cpython::{ |
|
14 | 14 | exc::ImportError, exc::TypeError, ObjectProtocol, PyClone, PyErr, |
|
15 | 15 | PyObject, PyResult, PyTuple, Python, PythonObject, |
|
16 | 16 | }; |
|
17 | 17 | use hg::revlog::{Node, RevlogIndex}; |
|
18 | 18 | use hg::{Graph, GraphError, Revision, WORKING_DIRECTORY_REVISION}; |
|
19 | 19 | use libc::{c_int, ssize_t}; |
|
20 | 20 | |
|
21 | 21 | const REVLOG_CABI_VERSION: c_int = 2; |
|
22 | 22 | |
|
23 | 23 | #[repr(C)] |
|
24 | 24 | pub struct Revlog_CAPI { |
|
25 | 25 | abi_version: c_int, |
|
26 | 26 | index_length: |
|
27 | 27 | unsafe extern "C" fn(index: *mut revlog_capi::RawPyObject) -> ssize_t, |
|
28 | 28 | index_node: unsafe extern "C" fn( |
|
29 | 29 | index: *mut revlog_capi::RawPyObject, |
|
30 | 30 | rev: ssize_t, |
|
31 | 31 | ) -> *const Node, |
|
32 | 32 | index_parents: unsafe extern "C" fn( |
|
33 | 33 | index: *mut revlog_capi::RawPyObject, |
|
34 | 34 | rev: c_int, |
|
35 | 35 | ps: *mut [c_int; 2], |
|
36 | 36 | ) -> c_int, |
|
37 | 37 | } |
|
38 | 38 | |
|
39 | 39 | py_capsule!( |
|
40 | 40 | from mercurial.cext.parsers import revlog_CAPI |
|
41 | 41 | as revlog_capi for Revlog_CAPI); |
|
42 | 42 | |
|
43 | 43 | /// A `Graph` backed up by objects and functions from revlog.c |
|
44 | 44 | /// |
|
45 | 45 | /// This implementation of the `Graph` trait, relies on (pointers to) |
|
46 | 46 | /// - the C index object (`index` member) |
|
47 | 47 | /// - the `index_get_parents()` function (`parents` member) |
|
48 | 48 | /// |
|
49 | 49 | /// # Safety |
|
50 | 50 | /// |
|
51 | 51 | /// The C index itself is mutable, and this Rust exposition is **not |
|
52 | 52 | /// protected by the GIL**, meaning that this construct isn't safe with respect |
|
53 | 53 | /// to Python threads. |
|
54 | 54 | /// |
|
55 | 55 | /// All callers of this `Index` must acquire the GIL and must not release it |
|
56 | 56 | /// while working. |
|
57 | 57 | /// |
|
58 | 58 | /// # TODO find a solution to make it GIL safe again. |
|
59 | 59 | /// |
|
60 | 60 | /// This is non trivial, and can wait until we have a clearer picture with |
|
61 | 61 | /// more Rust Mercurial constructs. |
|
62 | 62 | /// |
|
63 | 63 | /// One possibility would be to a `GILProtectedIndex` wrapper enclosing |
|
64 | 64 | /// a `Python<'p>` marker and have it be the one implementing the |
|
65 | 65 | /// `Graph` trait, but this would mean the `Graph` implementor would become |
|
66 | 66 | /// likely to change between subsequent method invocations of the `hg-core` |
|
67 | 67 | /// objects (a serious change of the `hg-core` API): |
|
68 | 68 | /// either exposing ways to mutate the `Graph`, or making it a non persistent |
|
69 | 69 | /// parameter in the relevant methods that need one. |
|
70 | 70 | /// |
|
71 | 71 | /// Another possibility would be to introduce an abstract lock handle into |
|
72 | 72 | /// the core API, that would be tied to `GILGuard` / `Python<'p>` |
|
73 | 73 | /// in the case of the `cpython` crate bindings yet could leave room for other |
|
74 | 74 | /// mechanisms in other contexts. |
|
75 | 75 | pub struct Index { |
|
76 | 76 | index: PyObject, |
|
77 | 77 | capi: &'static Revlog_CAPI, |
|
78 | 78 | } |
|
79 | 79 | |
|
80 | 80 | impl Index { |
|
81 | 81 | pub fn new(py: Python, index: PyObject) -> PyResult<Self> { |
|
82 | 82 | let capi = unsafe { revlog_capi::retrieve(py)? }; |
|
83 | 83 | if capi.abi_version != REVLOG_CABI_VERSION { |
|
84 | 84 | return Err(PyErr::new::<ImportError, _>( |
|
85 | 85 | py, |
|
86 | 86 | format!( |
|
87 | 87 | "ABI version mismatch: the C ABI revlog version {} \ |
|
88 | 88 | does not match the {} expected by Rust hg-cpython", |
|
89 | 89 | capi.abi_version, REVLOG_CABI_VERSION |
|
90 | 90 | ), |
|
91 | 91 | )); |
|
92 | 92 | } |
|
93 | 93 | let compat: u64 = index.getattr(py, "rust_ext_compat")?.extract(py)?; |
|
94 | 94 | if compat == 0 { |
|
95 | 95 | return Err(PyErr::new::<TypeError, _>( |
|
96 | 96 | py, |
|
97 | 97 | "index object not compatible with Rust", |
|
98 | 98 | )); |
|
99 | 99 | } |
|
100 | 100 | Ok(Index { index, capi }) |
|
101 | 101 | } |
|
102 | 102 | |
|
103 | 103 | /// return a reference to the CPython Index object in this Struct |
|
104 | 104 | pub fn inner(&self) -> &PyObject { |
|
105 | 105 | &self.index |
|
106 | 106 | } |
|
107 | 107 | |
|
108 | 108 | pub fn append(&mut self, py: Python, tup: PyTuple) -> PyResult<PyObject> { |
|
109 | 109 | self.index.call_method( |
|
110 | 110 | py, |
|
111 | 111 | "append", |
|
112 | 112 | PyTuple::new(py, &[tup.into_object()]), |
|
113 | 113 | None, |
|
114 | 114 | ) |
|
115 | 115 | } |
|
116 | 116 | } |
|
117 | 117 | |
|
118 | 118 | impl Clone for Index { |
|
119 | 119 | fn clone(&self) -> Self { |
|
120 | 120 | let guard = Python::acquire_gil(); |
|
121 | 121 | Index { |
|
122 | 122 | index: self.index.clone_ref(guard.python()), |
|
123 | 123 | capi: self.capi, |
|
124 | 124 | } |
|
125 | 125 | } |
|
126 | 126 | } |
|
127 | 127 | |
|
128 | 128 | impl PyClone for Index { |
|
129 | 129 | fn clone_ref(&self, py: Python) -> Self { |
|
130 | 130 | Index { |
|
131 | 131 | index: self.index.clone_ref(py), |
|
132 | 132 | capi: self.capi, |
|
133 | 133 | } |
|
134 | 134 | } |
|
135 | 135 | } |
|
136 | 136 | |
|
137 | 137 | impl Graph for Index { |
|
138 | 138 | /// wrap a call to the C extern parents function |
|
139 | 139 | fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> { |
|
140 | 140 | if rev == WORKING_DIRECTORY_REVISION { |
|
141 | 141 | return Err(GraphError::WorkingDirectoryUnsupported); |
|
142 | 142 | } |
|
143 | 143 | let mut res: [c_int; 2] = [0; 2]; |
|
144 | 144 | let code = unsafe { |
|
145 | 145 | (self.capi.index_parents)( |
|
146 | 146 | self.index.as_ptr(), |
|
147 | 147 | rev as c_int, |
|
148 | 148 | &mut res as *mut [c_int; 2], |
|
149 | 149 | ) |
|
150 | 150 | }; |
|
151 | 151 | match code { |
|
152 | 152 | 0 => Ok(res), |
|
153 | 153 | _ => Err(GraphError::ParentOutOfRange(rev)), |
|
154 | 154 | } |
|
155 | 155 | } |
|
156 | 156 | } |
|
157 | 157 | |
|
158 | impl vcsgraph::graph::Graph for Index { | |
|
159 | fn parents( | |
|
160 | &self, | |
|
161 | rev: Revision, | |
|
162 | ) -> Result<vcsgraph::graph::Parents, vcsgraph::graph::GraphReadError> | |
|
163 | { | |
|
164 | match Graph::parents(self, rev) { | |
|
165 | Ok(parents) => Ok(vcsgraph::graph::Parents(parents)), | |
|
166 | Err(GraphError::ParentOutOfRange(rev)) => { | |
|
167 | Err(vcsgraph::graph::GraphReadError::KeyedInvalidKey(rev)) | |
|
168 | } | |
|
169 | Err(GraphError::WorkingDirectoryUnsupported) => Err( | |
|
170 | vcsgraph::graph::GraphReadError::WorkingDirectoryUnsupported, | |
|
171 | ), | |
|
172 | } | |
|
173 | } | |
|
174 | } | |
|
175 | ||
|
158 | 176 | impl RevlogIndex for Index { |
|
159 | 177 | /// Note C return type is Py_ssize_t (hence signed), but we shall |
|
160 | 178 | /// force it to unsigned, because it's a length |
|
161 | 179 | fn len(&self) -> usize { |
|
162 | 180 | unsafe { (self.capi.index_length)(self.index.as_ptr()) as usize } |
|
163 | 181 | } |
|
164 | 182 | |
|
165 | 183 | fn node(&self, rev: Revision) -> Option<&Node> { |
|
166 | 184 | let raw = unsafe { |
|
167 | 185 | (self.capi.index_node)(self.index.as_ptr(), rev as ssize_t) |
|
168 | 186 | }; |
|
169 | 187 | if raw.is_null() { |
|
170 | 188 | None |
|
171 | 189 | } else { |
|
172 | 190 | // TODO it would be much better for the C layer to give us |
|
173 | 191 | // a length, since the hash length will change in the near |
|
174 | 192 | // future, but that's probably out of scope for the nodemap |
|
175 | 193 | // patch series. |
|
176 | 194 | // |
|
177 | 195 | // The root of that unsafety relies in the signature of |
|
178 | 196 | // `capi.index_node()` itself: returning a `Node` pointer |
|
179 | 197 | // whereas it's a `char *` in the C counterpart. |
|
180 | 198 | Some(unsafe { &*raw }) |
|
181 | 199 | } |
|
182 | 200 | } |
|
183 | 201 | } |
General Comments 0
You need to be logged in to leave comments.
Login now