Show More
@@ -5,12 +5,20 b'' | |||||
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:: |
|
8 | use crate::{ | |
|
9 | cindex, | |||
|
10 | utils::{node_from_py_bytes, node_from_py_object}, | |||
|
11 | }; | |||
9 | use cpython::{ |
|
12 | use cpython::{ | |
10 | exc::ValueError, ObjectProtocol, PyClone, PyDict, PyErr, PyModule, |
|
13 | exc::{IndexError, ValueError}, | |
11 | PyObject, PyResult, PyTuple, Python, PythonObject, ToPyObject, |
|
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 | use std::cell::RefCell; |
|
22 | use std::cell::RefCell; | |
15 |
|
23 | |||
16 | /// Return a Struct implementing the Graph trait |
|
24 | /// Return a Struct implementing the Graph trait | |
@@ -26,6 +34,7 b' pub(crate) fn pyindex_to_graph(' | |||||
26 |
|
34 | |||
27 | py_class!(pub class MixedIndex |py| { |
|
35 | py_class!(pub class MixedIndex |py| { | |
28 | data cindex: RefCell<cindex::Index>; |
|
36 | data cindex: RefCell<cindex::Index>; | |
|
37 | data nt: RefCell<Option<NodeTree>>; | |||
29 |
|
38 | |||
30 | def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> { |
|
39 | def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> { | |
31 | Self::new(py, cindex) |
|
40 | Self::new(py, cindex) | |
@@ -42,8 +51,99 b' py_class!(pub class MixedIndex |py| {' | |||||
42 | Ok(self.cindex(py).borrow().inner().clone_ref(py)) |
|
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 | // Reforwarded C index API |
|
145 | // Reforwarded C index API | |
|
146 | // | |||
47 |
|
147 | |||
48 | // index_methods (tp_methods). Same ordering as in revlog.c |
|
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 | self.call_cindex(py, "get", args, kw) |
|
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 | /// compute phases |
|
170 | /// compute phases | |
86 | def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> { |
|
171 | def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> { | |
87 | self.call_cindex(py, "computephasesmapsets", args, kw) |
|
172 | self.call_cindex(py, "computephasesmapsets", args, kw) | |
@@ -122,21 +207,6 b' py_class!(pub class MixedIndex |py| {' | |||||
122 | self.call_cindex(py, "slicechunktodensity", args, kw) |
|
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 | /// stats for the index |
|
210 | /// stats for the index | |
141 | def stats(&self, *args, **kw) -> PyResult<PyObject> { |
|
211 | def stats(&self, *args, **kw) -> PyResult<PyObject> { | |
142 | self.call_cindex(py, "stats", args, kw) |
|
212 | self.call_cindex(py, "stats", args, kw) | |
@@ -170,10 +240,6 b' py_class!(pub class MixedIndex |py| {' | |||||
170 | self.cindex(py).borrow().inner().set_item(py, key, value) |
|
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 | def __contains__(&self, item: PyObject) -> PyResult<bool> { |
|
243 | def __contains__(&self, item: PyObject) -> PyResult<bool> { | |
178 | // ObjectProtocol does not seem to provide contains(), so |
|
244 | // ObjectProtocol does not seem to provide contains(), so | |
179 | // this is an equivalent implementation of the index_contains() |
|
245 | // this is an equivalent implementation of the index_contains() | |
@@ -202,9 +268,41 b' impl MixedIndex {' | |||||
202 | Self::create_instance( |
|
268 | Self::create_instance( | |
203 | py, |
|
269 | py, | |
204 | RefCell::new(cindex::Index::new(py, cindex)?), |
|
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 | /// forward a method call to the underlying C index |
|
306 | /// forward a method call to the underlying C index | |
209 | fn call_cindex( |
|
307 | fn call_cindex( | |
210 | &self, |
|
308 | &self, |
General Comments 0
You need to be logged in to leave comments.
Login now