##// END OF EJS Templates
hg-cpython: implement vcsgraph::Graph for our Index...
pacien -
r49349:8e8737a1 default
parent child Browse files
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