Mercurial > hg
comparison rust/hg-cpython/src/revlog.rs @ 43961:b69d5f3a41d0
rust-index: add a struct wrapping the C index
Implementing the full index logic in one go is journey larger than we would
like.
To achieve a smoother transition, we start with a simple Rust wrapper that delegates
allwork to the current C implementation. Once we will have a fully working index
object in Rust, we can easily start using more and more Rust Code with it.
The object in this patch is functional and tested. However, multiple of the
currently existing rust (in the `hg-cpython` crate) requires a `Graph`. Right
now we build this `Graph` (as cindex::Index) using the C index passed as
a PyObject. They will have to be updated to be made compatible.
Differential Revision: https://phab.mercurial-scm.org/D7655
author | Georges Racinet <georges.racinet@octobus.net> |
---|---|
date | Mon, 23 Dec 2019 10:02:50 -0800 |
parents | f98f0e3ddaa1 |
children | 2728fcb8127c |
comparison
equal
deleted
inserted
replaced
43960:ab3fd8077f5e | 43961:b69d5f3a41d0 |
---|---|
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 use crate::cindex; | 8 use crate::cindex; |
9 use cpython::{PyObject, PyResult, Python}; | 9 use cpython::{ |
10 ObjectProtocol, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, | |
11 PythonObject, ToPyObject, | |
12 }; | |
13 use hg::Revision; | |
14 use std::cell::RefCell; | |
10 | 15 |
11 /// Return a Struct implementing the Graph trait | 16 /// Return a Struct implementing the Graph trait |
12 pub(crate) fn pyindex_to_graph( | 17 pub(crate) fn pyindex_to_graph( |
13 py: Python, | 18 py: Python, |
14 index: PyObject, | 19 index: PyObject, |
15 ) -> PyResult<cindex::Index> { | 20 ) -> PyResult<cindex::Index> { |
16 cindex::Index::new(py, index) | 21 cindex::Index::new(py, index) |
17 } | 22 } |
23 | |
24 py_class!(pub class MixedIndex |py| { | |
25 data cindex: RefCell<cindex::Index>; | |
26 | |
27 def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> { | |
28 Self::create_instance(py, RefCell::new( | |
29 cindex::Index::new(py, cindex)?)) | |
30 } | |
31 | |
32 | |
33 // Reforwarded C index API | |
34 | |
35 // index_methods (tp_methods). Same ordering as in revlog.c | |
36 | |
37 /// return the gca set of the given revs | |
38 def ancestors(&self, *args, **kw) -> PyResult<PyObject> { | |
39 self.call_cindex(py, "ancestors", args, kw) | |
40 } | |
41 | |
42 /// return the heads of the common ancestors of the given revs | |
43 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> { | |
44 self.call_cindex(py, "commonancestorsheads", args, kw) | |
45 } | |
46 | |
47 /// clear the index caches | |
48 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> { | |
49 self.call_cindex(py, "clearcaches", args, kw) | |
50 } | |
51 | |
52 /// get an index entry | |
53 def get(&self, *args, **kw) -> PyResult<PyObject> { | |
54 self.call_cindex(py, "get", args, kw) | |
55 } | |
56 | |
57 /// return `rev` associated with a node or None | |
58 def get_rev(&self, *args, **kw) -> PyResult<PyObject> { | |
59 self.call_cindex(py, "get_rev", args, kw) | |
60 } | |
61 | |
62 /// return True if the node exist in the index | |
63 def has_node(&self, *args, **kw) -> PyResult<PyObject> { | |
64 self.call_cindex(py, "has_node", args, kw) | |
65 } | |
66 | |
67 /// return `rev` associated with a node or raise RevlogError | |
68 def rev(&self, *args, **kw) -> PyResult<PyObject> { | |
69 self.call_cindex(py, "rev", args, kw) | |
70 } | |
71 | |
72 /// compute phases | |
73 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> { | |
74 self.call_cindex(py, "computephasesmapsets", args, kw) | |
75 } | |
76 | |
77 /// reachableroots | |
78 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> { | |
79 self.call_cindex(py, "reachableroots2", args, kw) | |
80 } | |
81 | |
82 /// get head revisions | |
83 def headrevs(&self, *args, **kw) -> PyResult<PyObject> { | |
84 self.call_cindex(py, "headrevs", args, kw) | |
85 } | |
86 | |
87 /// get filtered head revisions | |
88 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> { | |
89 self.call_cindex(py, "headrevsfiltered", args, kw) | |
90 } | |
91 | |
92 /// True if the object is a snapshot | |
93 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> { | |
94 self.call_cindex(py, "issnapshot", args, kw) | |
95 } | |
96 | |
97 /// Gather snapshot data in a cache dict | |
98 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> { | |
99 self.call_cindex(py, "findsnapshots", args, kw) | |
100 } | |
101 | |
102 /// determine revisions with deltas to reconstruct fulltext | |
103 def deltachain(&self, *args, **kw) -> PyResult<PyObject> { | |
104 self.call_cindex(py, "deltachain", args, kw) | |
105 } | |
106 | |
107 /// slice planned chunk read to reach a density threshold | |
108 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> { | |
109 self.call_cindex(py, "slicechunktodensity", args, kw) | |
110 } | |
111 | |
112 /// append an index entry | |
113 def append(&self, *args, **kw) -> PyResult<PyObject> { | |
114 self.call_cindex(py, "append", args, kw) | |
115 } | |
116 | |
117 /// match a potentially ambiguous node ID | |
118 def partialmatch(&self, *args, **kw) -> PyResult<PyObject> { | |
119 self.call_cindex(py, "partialmatch", args, kw) | |
120 } | |
121 | |
122 /// find length of shortest hex nodeid of a binary ID | |
123 def shortest(&self, *args, **kw) -> PyResult<PyObject> { | |
124 self.call_cindex(py, "shortest", args, kw) | |
125 } | |
126 | |
127 /// stats for the index | |
128 def stats(&self, *args, **kw) -> PyResult<PyObject> { | |
129 self.call_cindex(py, "stats", args, kw) | |
130 } | |
131 | |
132 // index_sequence_methods and index_mapping_methods. | |
133 // | |
134 // Since we call back through the high level Python API, | |
135 // there's no point making a distinction between index_get | |
136 // and index_getitem. | |
137 | |
138 def __len__(&self) -> PyResult<usize> { | |
139 self.cindex(py).borrow().inner().len(py) | |
140 } | |
141 | |
142 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> { | |
143 // this conversion seems needless, but that's actually because | |
144 // `index_getitem` does not handle conversion from PyLong, | |
145 // which expressions such as [e for e in index] internally use. | |
146 // Note that we don't seem to have a direct way to call | |
147 // PySequence_GetItem (does the job), which would be better for | |
148 // for performance | |
149 let key = match key.extract::<Revision>(py) { | |
150 Ok(rev) => rev.to_py_object(py).into_object(), | |
151 Err(_) => key, | |
152 }; | |
153 self.cindex(py).borrow().inner().get_item(py, key) | |
154 } | |
155 | |
156 def __setitem__(&self, key: PyObject, value: PyObject) -> PyResult<()> { | |
157 self.cindex(py).borrow().inner().set_item(py, key, value) | |
158 } | |
159 | |
160 def __delitem__(&self, key: PyObject) -> PyResult<()> { | |
161 self.cindex(py).borrow().inner().del_item(py, key) | |
162 } | |
163 | |
164 def __contains__(&self, item: PyObject) -> PyResult<bool> { | |
165 // ObjectProtocol does not seem to provide contains(), so | |
166 // this is an equivalent implementation of the index_contains() | |
167 // defined in revlog.c | |
168 let cindex = self.cindex(py).borrow(); | |
169 match item.extract::<Revision>(py) { | |
170 Ok(rev) => { | |
171 Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision) | |
172 } | |
173 Err(_) => { | |
174 cindex.inner().call_method( | |
175 py, | |
176 "has_node", | |
177 PyTuple::new(py, &[item]), | |
178 None)? | |
179 .extract(py) | |
180 } | |
181 } | |
182 } | |
183 | |
184 | |
185 }); | |
186 | |
187 impl MixedIndex { | |
188 /// forward a method call to the underlying C index | |
189 fn call_cindex( | |
190 &self, | |
191 py: Python, | |
192 name: &str, | |
193 args: &PyTuple, | |
194 kwargs: Option<&PyDict>, | |
195 ) -> PyResult<PyObject> { | |
196 self.cindex(py) | |
197 .borrow() | |
198 .inner() | |
199 .call_method(py, name, args, kwargs) | |
200 } | |
201 } | |
202 | |
203 /// Create the module, with __package__ given from parent | |
204 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | |
205 let dotted_name = &format!("{}.revlog", package); | |
206 let m = PyModule::new(py, dotted_name)?; | |
207 m.add(py, "__package__", package)?; | |
208 m.add(py, "__doc__", "RevLog - Rust implementations")?; | |
209 | |
210 m.add_class::<MixedIndex>(py)?; | |
211 | |
212 let sys = PyModule::import(py, "sys")?; | |
213 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; | |
214 sys_modules.set_item(py, dotted_name, &m)?; | |
215 | |
216 Ok(m) | |
217 } |