##// END OF EJS Templates
rust-nodemap: use proper Index API instead of using the C API...
Raphaël Gomès -
r44994:857cc792 default
parent child Browse files
Show More
@@ -5,12 +5,20 b''
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 use crate::cindex;
8 use crate::{
9 cindex,
10 utils::{node_from_py_bytes, node_from_py_object},
11 };
9 12 use cpython::{
10 exc::ValueError, ObjectProtocol, PyClone, PyDict, PyErr, PyModule,
11 PyObject, PyResult, PyTuple, Python, PythonObject, ToPyObject,
13 exc::{IndexError, ValueError},
14 ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyModule, PyObject,
15 PyResult, PyString, PyTuple, Python, PythonObject, ToPyObject,
12 16 };
13 use hg::{nodemap::NodeMapError, NodeError, Revision};
17 use hg::{
18 nodemap::{NodeMapError, NodeTree},
19 revlog::{nodemap::NodeMap, RevlogIndex},
20 NodeError, Revision,
21 };
14 22 use std::cell::RefCell;
15 23
16 24 /// Return a Struct implementing the Graph trait
@@ -26,6 +34,7 b' pub(crate) fn pyindex_to_graph('
26 34
27 35 py_class!(pub class MixedIndex |py| {
28 36 data cindex: RefCell<cindex::Index>;
37 data nt: RefCell<Option<NodeTree>>;
29 38
30 39 def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> {
31 40 Self::new(py, cindex)
@@ -42,8 +51,99 b' py_class!(pub class MixedIndex |py| {'
42 51 Ok(self.cindex(py).borrow().inner().clone_ref(py))
43 52 }
44 53
54 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
45 55
56 /// Return Revision if found, raises a bare `error.RevlogError`
57 /// in case of ambiguity, same as C version does
58 def get_rev(&self, node: PyBytes) -> PyResult<Option<Revision>> {
59 let opt = self.get_nodetree(py)?.borrow();
60 let nt = opt.as_ref().unwrap();
61 let idx = &*self.cindex(py).borrow();
62 let node = node_from_py_bytes(py, &node)?;
63 nt.find_bin(idx, (&node).into()).map_err(|e| nodemap_error(py, e))
64 }
65
66 /// same as `get_rev()` but raises a bare `error.RevlogError` if node
67 /// is not found.
68 ///
69 /// No need to repeat `node` in the exception, `mercurial/revlog.py`
70 /// will catch and rewrap with it
71 def rev(&self, node: PyBytes) -> PyResult<Revision> {
72 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
73 }
74
75 /// return True if the node exist in the index
76 def has_node(&self, node: PyBytes) -> PyResult<bool> {
77 self.get_rev(py, node).map(|opt| opt.is_some())
78 }
79
80 /// find length of shortest hex nodeid of a binary ID
81 def shortest(&self, node: PyBytes) -> PyResult<usize> {
82 let opt = self.get_nodetree(py)?.borrow();
83 let nt = opt.as_ref().unwrap();
84 let idx = &*self.cindex(py).borrow();
85 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
86 {
87 Ok(Some(l)) => Ok(l),
88 Ok(None) => Err(revlog_error(py)),
89 Err(e) => Err(nodemap_error(py, e)),
90 }
91 }
92
93 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
94 let opt = self.get_nodetree(py)?.borrow();
95 let nt = opt.as_ref().unwrap();
96 let idx = &*self.cindex(py).borrow();
97
98 let node_as_string = if cfg!(feature = "python3-sys") {
99 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
100 }
101 else {
102 let node = node.extract::<PyBytes>(py)?;
103 String::from_utf8_lossy(node.data(py)).to_string()
104 };
105
106 nt.find_hex(idx, &node_as_string)
107 // TODO make an inner API returning the node directly
108 .map(|opt| opt.map(
109 |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
110 .map_err(|e| nodemap_error(py, e))
111
112 }
113
114 /// append an index entry
115 def append(&self, tup: PyTuple) -> PyResult<PyObject> {
116 if tup.len(py) < 8 {
117 // this is better than the panic promised by tup.get_item()
118 return Err(
119 PyErr::new::<IndexError, _>(py, "tuple index out of range"))
120 }
121 let node_bytes = tup.get_item(py, 7).extract(py)?;
122 let node = node_from_py_object(py, &node_bytes)?;
123
124 let mut idx = self.cindex(py).borrow_mut();
125 let rev = idx.len() as Revision;
126
127 idx.append(py, tup)?;
128 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
129 .insert(&*idx, &node, rev)
130 .map_err(|e| nodemap_error(py, e))?;
131 Ok(py.None())
132 }
133
134 def __delitem__(&self, key: PyObject) -> PyResult<()> {
135 // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
136 self.cindex(py).borrow().inner().del_item(py, key)?;
137 let mut opt = self.get_nodetree(py)?.borrow_mut();
138 let mut nt = opt.as_mut().unwrap();
139 nt.invalidate_all();
140 self.fill_nodemap(py, &mut nt)?;
141 Ok(())
142 }
143
144 //
46 145 // Reforwarded C index API
146 //
47 147
48 148 // index_methods (tp_methods). Same ordering as in revlog.c
49 149
@@ -67,21 +167,6 b' py_class!(pub class MixedIndex |py| {'
67 167 self.call_cindex(py, "get", args, kw)
68 168 }
69 169
70 /// return `rev` associated with a node or None
71 def get_rev(&self, *args, **kw) -> PyResult<PyObject> {
72 self.call_cindex(py, "get_rev", args, kw)
73 }
74
75 /// return True if the node exist in the index
76 def has_node(&self, *args, **kw) -> PyResult<PyObject> {
77 self.call_cindex(py, "has_node", args, kw)
78 }
79
80 /// return `rev` associated with a node or raise RevlogError
81 def rev(&self, *args, **kw) -> PyResult<PyObject> {
82 self.call_cindex(py, "rev", args, kw)
83 }
84
85 170 /// compute phases
86 171 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
87 172 self.call_cindex(py, "computephasesmapsets", args, kw)
@@ -122,21 +207,6 b' py_class!(pub class MixedIndex |py| {'
122 207 self.call_cindex(py, "slicechunktodensity", args, kw)
123 208 }
124 209
125 /// append an index entry
126 def append(&self, *args, **kw) -> PyResult<PyObject> {
127 self.call_cindex(py, "append", args, kw)
128 }
129
130 /// match a potentially ambiguous node ID
131 def partialmatch(&self, *args, **kw) -> PyResult<PyObject> {
132 self.call_cindex(py, "partialmatch", args, kw)
133 }
134
135 /// find length of shortest hex nodeid of a binary ID
136 def shortest(&self, *args, **kw) -> PyResult<PyObject> {
137 self.call_cindex(py, "shortest", args, kw)
138 }
139
140 210 /// stats for the index
141 211 def stats(&self, *args, **kw) -> PyResult<PyObject> {
142 212 self.call_cindex(py, "stats", args, kw)
@@ -170,10 +240,6 b' py_class!(pub class MixedIndex |py| {'
170 240 self.cindex(py).borrow().inner().set_item(py, key, value)
171 241 }
172 242
173 def __delitem__(&self, key: PyObject) -> PyResult<()> {
174 self.cindex(py).borrow().inner().del_item(py, key)
175 }
176
177 243 def __contains__(&self, item: PyObject) -> PyResult<bool> {
178 244 // ObjectProtocol does not seem to provide contains(), so
179 245 // this is an equivalent implementation of the index_contains()
@@ -202,9 +268,41 b' impl MixedIndex {'
202 268 Self::create_instance(
203 269 py,
204 270 RefCell::new(cindex::Index::new(py, cindex)?),
271 RefCell::new(None),
205 272 )
206 273 }
207 274
275 /// This is scaffolding at this point, but it could also become
276 /// a way to start a persistent nodemap or perform a
277 /// vacuum / repack operation
278 fn fill_nodemap(
279 &self,
280 py: Python,
281 nt: &mut NodeTree,
282 ) -> PyResult<PyObject> {
283 let index = self.cindex(py).borrow();
284 for r in 0..index.len() {
285 let rev = r as Revision;
286 // in this case node() won't ever return None
287 nt.insert(&*index, index.node(rev).unwrap(), rev)
288 .map_err(|e| nodemap_error(py, e))?
289 }
290 Ok(py.None())
291 }
292
293 fn get_nodetree<'a>(
294 &'a self,
295 py: Python<'a>,
296 ) -> PyResult<&'a RefCell<Option<NodeTree>>> {
297 if self.nt(py).borrow().is_none() {
298 let readonly = Box::new(Vec::new());
299 let mut nt = NodeTree::load_bytes(readonly, 0);
300 self.fill_nodemap(py, &mut nt)?;
301 self.nt(py).borrow_mut().replace(nt);
302 }
303 Ok(self.nt(py))
304 }
305
208 306 /// forward a method call to the underlying C index
209 307 fn call_cindex(
210 308 &self,
General Comments 0
You need to be logged in to leave comments. Login now