Show More
@@ -1,934 +1,928 b'' | |||||
1 | // revlog.rs |
|
1 | // revlog.rs | |
2 | // |
|
2 | // | |
3 | // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net> |
|
3 | // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net> | |
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::{ |
|
8 | use crate::{ | |
9 | cindex, |
|
9 | cindex, | |
10 | conversion::rev_pyiter_collect, |
|
10 | conversion::rev_pyiter_collect, | |
11 | exceptions::GraphError, |
|
|||
12 | utils::{node_from_py_bytes, node_from_py_object}, |
|
11 | utils::{node_from_py_bytes, node_from_py_object}, | |
13 | PyRevision, |
|
12 | PyRevision, | |
14 | }; |
|
13 | }; | |
15 | use cpython::{ |
|
14 | use cpython::{ | |
16 | buffer::{Element, PyBuffer}, |
|
15 | buffer::{Element, PyBuffer}, | |
17 | exc::{IndexError, ValueError}, |
|
16 | exc::{IndexError, ValueError}, | |
18 | ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList, |
|
17 | ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList, | |
19 | PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python, |
|
18 | PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python, | |
20 | PythonObject, ToPyObject, |
|
19 | PythonObject, ToPyObject, | |
21 | }; |
|
20 | }; | |
22 | use hg::{ |
|
21 | use hg::{ | |
23 | errors::HgError, |
|
22 | errors::HgError, | |
24 | index::{IndexHeader, RevisionDataParams, SnapshotsCache}, |
|
23 | index::{IndexHeader, RevisionDataParams, SnapshotsCache}, | |
25 | nodemap::{Block, NodeMapError, NodeTree}, |
|
24 | nodemap::{Block, NodeMapError, NodeTree}, | |
26 | revlog::{nodemap::NodeMap, NodePrefix, RevlogError, RevlogIndex}, |
|
25 | revlog::{nodemap::NodeMap, NodePrefix, RevlogError, RevlogIndex}, | |
27 | BaseRevision, Revision, UncheckedRevision, NULL_REVISION, |
|
26 | BaseRevision, Revision, UncheckedRevision, NULL_REVISION, | |
28 | }; |
|
27 | }; | |
29 | use std::cell::RefCell; |
|
28 | use std::cell::RefCell; | |
30 |
|
29 | |||
31 | /// Return a Struct implementing the Graph trait |
|
30 | /// Return a Struct implementing the Graph trait | |
32 | pub(crate) fn pyindex_to_graph( |
|
31 | pub(crate) fn pyindex_to_graph( | |
33 | py: Python, |
|
32 | py: Python, | |
34 | index: PyObject, |
|
33 | index: PyObject, | |
35 | ) -> PyResult<cindex::Index> { |
|
34 | ) -> PyResult<cindex::Index> { | |
36 | match index.extract::<MixedIndex>(py) { |
|
35 | match index.extract::<MixedIndex>(py) { | |
37 | Ok(midx) => Ok(midx.clone_cindex(py)), |
|
36 | Ok(midx) => Ok(midx.clone_cindex(py)), | |
38 | Err(_) => cindex::Index::new(py, index), |
|
37 | Err(_) => cindex::Index::new(py, index), | |
39 | } |
|
38 | } | |
40 | } |
|
39 | } | |
41 |
|
40 | |||
42 | py_class!(pub class MixedIndex |py| { |
|
41 | py_class!(pub class MixedIndex |py| { | |
43 | data cindex: RefCell<cindex::Index>; |
|
42 | data cindex: RefCell<cindex::Index>; | |
44 | data index: RefCell<hg::index::Index>; |
|
43 | data index: RefCell<hg::index::Index>; | |
45 | data nt: RefCell<Option<NodeTree>>; |
|
44 | data nt: RefCell<Option<NodeTree>>; | |
46 | data docket: RefCell<Option<PyObject>>; |
|
45 | data docket: RefCell<Option<PyObject>>; | |
47 | // Holds a reference to the mmap'ed persistent nodemap data |
|
46 | // Holds a reference to the mmap'ed persistent nodemap data | |
48 | data nodemap_mmap: RefCell<Option<PyBuffer>>; |
|
47 | data nodemap_mmap: RefCell<Option<PyBuffer>>; | |
49 | // Holds a reference to the mmap'ed persistent index data |
|
48 | // Holds a reference to the mmap'ed persistent index data | |
50 | data index_mmap: RefCell<Option<PyBuffer>>; |
|
49 | data index_mmap: RefCell<Option<PyBuffer>>; | |
51 |
|
50 | |||
52 | def __new__( |
|
51 | def __new__( | |
53 | _cls, |
|
52 | _cls, | |
54 | cindex: PyObject, |
|
53 | cindex: PyObject, | |
55 | data: PyObject, |
|
54 | data: PyObject, | |
56 | default_header: u32, |
|
55 | default_header: u32, | |
57 | ) -> PyResult<MixedIndex> { |
|
56 | ) -> PyResult<MixedIndex> { | |
58 | Self::new(py, cindex, data, default_header) |
|
57 | Self::new(py, cindex, data, default_header) | |
59 | } |
|
58 | } | |
60 |
|
59 | |||
61 | /// Compatibility layer used for Python consumers needing access to the C index |
|
60 | /// Compatibility layer used for Python consumers needing access to the C index | |
62 | /// |
|
61 | /// | |
63 | /// Only use case so far is `scmutil.shortesthexnodeidprefix`, |
|
62 | /// Only use case so far is `scmutil.shortesthexnodeidprefix`, | |
64 | /// that may need to build a custom `nodetree`, based on a specified revset. |
|
63 | /// that may need to build a custom `nodetree`, based on a specified revset. | |
65 | /// With a Rust implementation of the nodemap, we will be able to get rid of |
|
64 | /// With a Rust implementation of the nodemap, we will be able to get rid of | |
66 | /// this, by exposing our own standalone nodemap class, |
|
65 | /// this, by exposing our own standalone nodemap class, | |
67 | /// ready to accept `MixedIndex`. |
|
66 | /// ready to accept `MixedIndex`. | |
68 | def get_cindex(&self) -> PyResult<PyObject> { |
|
67 | def get_cindex(&self) -> PyResult<PyObject> { | |
69 | Ok(self.cindex(py).borrow().inner().clone_ref(py)) |
|
68 | Ok(self.cindex(py).borrow().inner().clone_ref(py)) | |
70 | } |
|
69 | } | |
71 |
|
70 | |||
72 | // Index API involving nodemap, as defined in mercurial/pure/parsers.py |
|
71 | // Index API involving nodemap, as defined in mercurial/pure/parsers.py | |
73 |
|
72 | |||
74 | /// Return Revision if found, raises a bare `error.RevlogError` |
|
73 | /// Return Revision if found, raises a bare `error.RevlogError` | |
75 | /// in case of ambiguity, same as C version does |
|
74 | /// in case of ambiguity, same as C version does | |
76 | def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> { |
|
75 | def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> { | |
77 | let opt = self.get_nodetree(py)?.borrow(); |
|
76 | let opt = self.get_nodetree(py)?.borrow(); | |
78 | let nt = opt.as_ref().unwrap(); |
|
77 | let nt = opt.as_ref().unwrap(); | |
79 | let idx = &*self.cindex(py).borrow(); |
|
78 | let idx = &*self.cindex(py).borrow(); | |
80 | let ridx = &*self.index(py).borrow(); |
|
79 | let ridx = &*self.index(py).borrow(); | |
81 | let node = node_from_py_bytes(py, &node)?; |
|
80 | let node = node_from_py_bytes(py, &node)?; | |
82 | let rust_rev = |
|
81 | let rust_rev = | |
83 | nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?; |
|
82 | nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?; | |
84 | let c_rev = |
|
83 | let c_rev = | |
85 | nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?; |
|
84 | nt.find_bin(idx, node.into()).map_err(|e| nodemap_error(py, e))?; | |
86 | assert_eq!(rust_rev, c_rev); |
|
85 | assert_eq!(rust_rev, c_rev); | |
87 | Ok(rust_rev.map(Into::into)) |
|
86 | Ok(rust_rev.map(Into::into)) | |
88 |
|
87 | |||
89 | } |
|
88 | } | |
90 |
|
89 | |||
91 | /// same as `get_rev()` but raises a bare `error.RevlogError` if node |
|
90 | /// same as `get_rev()` but raises a bare `error.RevlogError` if node | |
92 | /// is not found. |
|
91 | /// is not found. | |
93 | /// |
|
92 | /// | |
94 | /// No need to repeat `node` in the exception, `mercurial/revlog.py` |
|
93 | /// No need to repeat `node` in the exception, `mercurial/revlog.py` | |
95 | /// will catch and rewrap with it |
|
94 | /// will catch and rewrap with it | |
96 | def rev(&self, node: PyBytes) -> PyResult<PyRevision> { |
|
95 | def rev(&self, node: PyBytes) -> PyResult<PyRevision> { | |
97 | self.get_rev(py, node)?.ok_or_else(|| revlog_error(py)) |
|
96 | self.get_rev(py, node)?.ok_or_else(|| revlog_error(py)) | |
98 | } |
|
97 | } | |
99 |
|
98 | |||
100 | /// return True if the node exist in the index |
|
99 | /// return True if the node exist in the index | |
101 | def has_node(&self, node: PyBytes) -> PyResult<bool> { |
|
100 | def has_node(&self, node: PyBytes) -> PyResult<bool> { | |
102 | // TODO OPTIM we could avoid a needless conversion here, |
|
101 | // TODO OPTIM we could avoid a needless conversion here, | |
103 | // to do when scaffolding for pure Rust switch is removed, |
|
102 | // to do when scaffolding for pure Rust switch is removed, | |
104 | // as `get_rev()` currently does the necessary assertions |
|
103 | // as `get_rev()` currently does the necessary assertions | |
105 | self.get_rev(py, node).map(|opt| opt.is_some()) |
|
104 | self.get_rev(py, node).map(|opt| opt.is_some()) | |
106 | } |
|
105 | } | |
107 |
|
106 | |||
108 | /// find length of shortest hex nodeid of a binary ID |
|
107 | /// find length of shortest hex nodeid of a binary ID | |
109 | def shortest(&self, node: PyBytes) -> PyResult<usize> { |
|
108 | def shortest(&self, node: PyBytes) -> PyResult<usize> { | |
110 | let opt = self.get_nodetree(py)?.borrow(); |
|
109 | let opt = self.get_nodetree(py)?.borrow(); | |
111 | let nt = opt.as_ref().unwrap(); |
|
110 | let nt = opt.as_ref().unwrap(); | |
112 | let idx = &*self.index(py).borrow(); |
|
111 | let idx = &*self.index(py).borrow(); | |
113 | match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?) |
|
112 | match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?) | |
114 | { |
|
113 | { | |
115 | Ok(Some(l)) => Ok(l), |
|
114 | Ok(Some(l)) => Ok(l), | |
116 | Ok(None) => Err(revlog_error(py)), |
|
115 | Ok(None) => Err(revlog_error(py)), | |
117 | Err(e) => Err(nodemap_error(py, e)), |
|
116 | Err(e) => Err(nodemap_error(py, e)), | |
118 | } |
|
117 | } | |
119 | } |
|
118 | } | |
120 |
|
119 | |||
121 | def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> { |
|
120 | def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> { | |
122 | let opt = self.get_nodetree(py)?.borrow(); |
|
121 | let opt = self.get_nodetree(py)?.borrow(); | |
123 | let nt = opt.as_ref().unwrap(); |
|
122 | let nt = opt.as_ref().unwrap(); | |
124 | let idx = &*self.index(py).borrow(); |
|
123 | let idx = &*self.index(py).borrow(); | |
125 |
|
124 | |||
126 | let node_as_string = if cfg!(feature = "python3-sys") { |
|
125 | let node_as_string = if cfg!(feature = "python3-sys") { | |
127 | node.cast_as::<PyString>(py)?.to_string(py)?.to_string() |
|
126 | node.cast_as::<PyString>(py)?.to_string(py)?.to_string() | |
128 | } |
|
127 | } | |
129 | else { |
|
128 | else { | |
130 | let node = node.extract::<PyBytes>(py)?; |
|
129 | let node = node.extract::<PyBytes>(py)?; | |
131 | String::from_utf8_lossy(node.data(py)).to_string() |
|
130 | String::from_utf8_lossy(node.data(py)).to_string() | |
132 | }; |
|
131 | }; | |
133 |
|
132 | |||
134 | let prefix = NodePrefix::from_hex(&node_as_string) |
|
133 | let prefix = NodePrefix::from_hex(&node_as_string) | |
135 | .map_err(|_| PyErr::new::<ValueError, _>( |
|
134 | .map_err(|_| PyErr::new::<ValueError, _>( | |
136 | py, format!("Invalid node or prefix '{}'", node_as_string)) |
|
135 | py, format!("Invalid node or prefix '{}'", node_as_string)) | |
137 | )?; |
|
136 | )?; | |
138 |
|
137 | |||
139 | nt.find_bin(idx, prefix) |
|
138 | nt.find_bin(idx, prefix) | |
140 | // TODO make an inner API returning the node directly |
|
139 | // TODO make an inner API returning the node directly | |
141 | .map(|opt| opt.map( |
|
140 | .map(|opt| opt.map( | |
142 | |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes()))) |
|
141 | |rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes()))) | |
143 | .map_err(|e| nodemap_error(py, e)) |
|
142 | .map_err(|e| nodemap_error(py, e)) | |
144 |
|
143 | |||
145 | } |
|
144 | } | |
146 |
|
145 | |||
147 | /// append an index entry |
|
146 | /// append an index entry | |
148 | def append(&self, tup: PyTuple) -> PyResult<PyObject> { |
|
147 | def append(&self, tup: PyTuple) -> PyResult<PyObject> { | |
149 | if tup.len(py) < 8 { |
|
148 | if tup.len(py) < 8 { | |
150 | // this is better than the panic promised by tup.get_item() |
|
149 | // this is better than the panic promised by tup.get_item() | |
151 | return Err( |
|
150 | return Err( | |
152 | PyErr::new::<IndexError, _>(py, "tuple index out of range")) |
|
151 | PyErr::new::<IndexError, _>(py, "tuple index out of range")) | |
153 | } |
|
152 | } | |
154 | let node_bytes = tup.get_item(py, 7).extract(py)?; |
|
153 | let node_bytes = tup.get_item(py, 7).extract(py)?; | |
155 | let node = node_from_py_object(py, &node_bytes)?; |
|
154 | let node = node_from_py_object(py, &node_bytes)?; | |
156 |
|
155 | |||
157 | let rev = self.len(py)? as BaseRevision; |
|
156 | let rev = self.len(py)? as BaseRevision; | |
158 | let mut idx = self.cindex(py).borrow_mut(); |
|
157 | let mut idx = self.cindex(py).borrow_mut(); | |
159 |
|
158 | |||
160 | // This is ok since we will just add the revision to the index |
|
159 | // This is ok since we will just add the revision to the index | |
161 | let rev = Revision(rev); |
|
160 | let rev = Revision(rev); | |
162 | idx.append(py, tup.clone_ref(py))?; |
|
161 | idx.append(py, tup.clone_ref(py))?; | |
163 | self.index(py) |
|
162 | self.index(py) | |
164 | .borrow_mut() |
|
163 | .borrow_mut() | |
165 | .append(py_tuple_to_revision_data_params(py, tup)?) |
|
164 | .append(py_tuple_to_revision_data_params(py, tup)?) | |
166 | .unwrap(); |
|
165 | .unwrap(); | |
167 | self.get_nodetree(py)?.borrow_mut().as_mut().unwrap() |
|
166 | self.get_nodetree(py)?.borrow_mut().as_mut().unwrap() | |
168 | .insert(&*idx, &node, rev) |
|
167 | .insert(&*idx, &node, rev) | |
169 | .map_err(|e| nodemap_error(py, e))?; |
|
168 | .map_err(|e| nodemap_error(py, e))?; | |
170 | Ok(py.None()) |
|
169 | Ok(py.None()) | |
171 | } |
|
170 | } | |
172 |
|
171 | |||
173 | def __delitem__(&self, key: PyObject) -> PyResult<()> { |
|
172 | def __delitem__(&self, key: PyObject) -> PyResult<()> { | |
174 | // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]` |
|
173 | // __delitem__ is both for `del idx[r]` and `del idx[r1:r2]` | |
175 | self.cindex(py).borrow().inner().del_item(py, &key)?; |
|
174 | self.cindex(py).borrow().inner().del_item(py, &key)?; | |
176 | let start = key.getattr(py, "start")?; |
|
175 | let start = key.getattr(py, "start")?; | |
177 | let start = UncheckedRevision(start.extract(py)?); |
|
176 | let start = UncheckedRevision(start.extract(py)?); | |
178 | let start = self.index(py) |
|
177 | let start = self.index(py) | |
179 | .borrow() |
|
178 | .borrow() | |
180 | .check_revision(start) |
|
179 | .check_revision(start) | |
181 | .ok_or_else(|| { |
|
180 | .ok_or_else(|| { | |
182 | nodemap_error(py, NodeMapError::RevisionNotInIndex(start)) |
|
181 | nodemap_error(py, NodeMapError::RevisionNotInIndex(start)) | |
183 | })?; |
|
182 | })?; | |
184 | self.index(py).borrow_mut().remove(start).unwrap(); |
|
183 | self.index(py).borrow_mut().remove(start).unwrap(); | |
185 | let mut opt = self.get_nodetree(py)?.borrow_mut(); |
|
184 | let mut opt = self.get_nodetree(py)?.borrow_mut(); | |
186 | let nt = opt.as_mut().unwrap(); |
|
185 | let nt = opt.as_mut().unwrap(); | |
187 | nt.invalidate_all(); |
|
186 | nt.invalidate_all(); | |
188 | self.fill_nodemap(py, nt)?; |
|
187 | self.fill_nodemap(py, nt)?; | |
189 | Ok(()) |
|
188 | Ok(()) | |
190 | } |
|
189 | } | |
191 |
|
190 | |||
192 | // |
|
191 | // | |
193 | // Reforwarded C index API |
|
192 | // Reforwarded C index API | |
194 | // |
|
193 | // | |
195 |
|
194 | |||
196 | // index_methods (tp_methods). Same ordering as in revlog.c |
|
195 | // index_methods (tp_methods). Same ordering as in revlog.c | |
197 |
|
196 | |||
198 | /// return the gca set of the given revs |
|
197 | /// return the gca set of the given revs | |
199 | def ancestors(&self, *args, **kw) -> PyResult<PyObject> { |
|
198 | def ancestors(&self, *args, **kw) -> PyResult<PyObject> { | |
200 | self.call_cindex(py, "ancestors", args, kw) |
|
199 | self.call_cindex(py, "ancestors", args, kw) | |
201 | } |
|
200 | } | |
202 |
|
201 | |||
203 | /// return the heads of the common ancestors of the given revs |
|
202 | /// return the heads of the common ancestors of the given revs | |
204 | def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> { |
|
203 | def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> { | |
205 | self.call_cindex(py, "commonancestorsheads", args, kw) |
|
204 | self.call_cindex(py, "commonancestorsheads", args, kw) | |
206 | } |
|
205 | } | |
207 |
|
206 | |||
208 | /// Clear the index caches and inner py_class data. |
|
207 | /// Clear the index caches and inner py_class data. | |
209 | /// It is Python's responsibility to call `update_nodemap_data` again. |
|
208 | /// It is Python's responsibility to call `update_nodemap_data` again. | |
210 | def clearcaches(&self, *args, **kw) -> PyResult<PyObject> { |
|
209 | def clearcaches(&self, *args, **kw) -> PyResult<PyObject> { | |
211 | self.nt(py).borrow_mut().take(); |
|
210 | self.nt(py).borrow_mut().take(); | |
212 | self.docket(py).borrow_mut().take(); |
|
211 | self.docket(py).borrow_mut().take(); | |
213 | self.nodemap_mmap(py).borrow_mut().take(); |
|
212 | self.nodemap_mmap(py).borrow_mut().take(); | |
214 | self.index(py).borrow_mut().clear_caches(); |
|
213 | self.index(py).borrow_mut().clear_caches(); | |
215 | self.call_cindex(py, "clearcaches", args, kw) |
|
214 | self.call_cindex(py, "clearcaches", args, kw) | |
216 | } |
|
215 | } | |
217 |
|
216 | |||
218 | /// return the raw binary string representing a revision |
|
217 | /// return the raw binary string representing a revision | |
219 | def entry_binary(&self, *args, **kw) -> PyResult<PyObject> { |
|
218 | def entry_binary(&self, *args, **kw) -> PyResult<PyObject> { | |
220 | let rindex = self.index(py).borrow(); |
|
219 | let rindex = self.index(py).borrow(); | |
221 | let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?); |
|
220 | let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?); | |
222 | let rust_bytes = rindex.check_revision(rev).and_then( |
|
221 | let rust_bytes = rindex.check_revision(rev).and_then( | |
223 | |r| rindex.entry_binary(r)) |
|
222 | |r| rindex.entry_binary(r)) | |
224 | .ok_or_else(|| rev_not_in_index(py, rev))?; |
|
223 | .ok_or_else(|| rev_not_in_index(py, rev))?; | |
225 | let rust_res = PyBytes::new(py, rust_bytes).into_object(); |
|
224 | let rust_res = PyBytes::new(py, rust_bytes).into_object(); | |
226 |
|
225 | |||
227 | let c_res = self.call_cindex(py, "entry_binary", args, kw)?; |
|
226 | let c_res = self.call_cindex(py, "entry_binary", args, kw)?; | |
228 | assert_py_eq(py, "entry_binary", &rust_res, &c_res)?; |
|
227 | assert_py_eq(py, "entry_binary", &rust_res, &c_res)?; | |
229 | Ok(rust_res) |
|
228 | Ok(rust_res) | |
230 | } |
|
229 | } | |
231 |
|
230 | |||
232 | /// return a binary packed version of the header |
|
231 | /// return a binary packed version of the header | |
233 | def pack_header(&self, *args, **kw) -> PyResult<PyObject> { |
|
232 | def pack_header(&self, *args, **kw) -> PyResult<PyObject> { | |
234 | let rindex = self.index(py).borrow(); |
|
233 | let rindex = self.index(py).borrow(); | |
235 | let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?); |
|
234 | let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?); | |
236 | let rust_res = PyBytes::new(py, &packed).into_object(); |
|
235 | let rust_res = PyBytes::new(py, &packed).into_object(); | |
237 |
|
236 | |||
238 | let c_res = self.call_cindex(py, "pack_header", args, kw)?; |
|
237 | let c_res = self.call_cindex(py, "pack_header", args, kw)?; | |
239 | assert_py_eq(py, "pack_header", &rust_res, &c_res)?; |
|
238 | assert_py_eq(py, "pack_header", &rust_res, &c_res)?; | |
240 | Ok(rust_res) |
|
239 | Ok(rust_res) | |
241 | } |
|
240 | } | |
242 |
|
241 | |||
243 | /// compute phases |
|
242 | /// compute phases | |
244 | def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> { |
|
243 | def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> { | |
245 | self.call_cindex(py, "computephasesmapsets", args, kw) |
|
244 | self.call_cindex(py, "computephasesmapsets", args, kw) | |
246 | } |
|
245 | } | |
247 |
|
246 | |||
248 | /// reachableroots |
|
247 | /// reachableroots | |
249 | def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> { |
|
248 | def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> { | |
250 | self.call_cindex(py, "reachableroots2", args, kw) |
|
249 | self.call_cindex(py, "reachableroots2", args, kw) | |
251 | } |
|
250 | } | |
252 |
|
251 | |||
253 | /// get head revisions |
|
252 | /// get head revisions | |
254 | def headrevs(&self, *args, **kw) -> PyResult<PyObject> { |
|
253 | def headrevs(&self, *args, **kw) -> PyResult<PyObject> { | |
255 | let rust_res = self.inner_headrevs(py)?; |
|
254 | let rust_res = self.inner_headrevs(py)?; | |
256 |
|
255 | |||
257 | let c_res = self.call_cindex(py, "headrevs", args, kw)?; |
|
256 | let c_res = self.call_cindex(py, "headrevs", args, kw)?; | |
258 | assert_py_eq(py, "headrevs", &rust_res, &c_res)?; |
|
257 | assert_py_eq(py, "headrevs", &rust_res, &c_res)?; | |
259 | Ok(rust_res) |
|
258 | Ok(rust_res) | |
260 | } |
|
259 | } | |
261 |
|
260 | |||
262 | /// get filtered head revisions |
|
261 | /// get filtered head revisions | |
263 | def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> { |
|
262 | def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> { | |
264 | let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?; |
|
263 | let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?; | |
265 | let c_res = self.call_cindex(py, "headrevsfiltered", args, kw)?; |
|
264 | let c_res = self.call_cindex(py, "headrevsfiltered", args, kw)?; | |
266 | assert_eq!( |
|
265 | ||
267 | rust_res.len(), |
|
266 | assert_py_eq(py, "headrevsfiltered", &rust_res, &c_res)?; | |
268 | c_res.len(py)?, |
|
267 | Ok(rust_res) | |
269 | "filtered heads differ {:?} {}", |
|
|||
270 | rust_res, |
|
|||
271 | c_res |
|
|||
272 | ); |
|
|||
273 | for (index, rev) in rust_res.iter().enumerate() { |
|
|||
274 | let c_rev: BaseRevision = c_res.get_item(py, index)?.extract(py)?; |
|
|||
275 | assert_eq!(c_rev, rev.0); |
|
|||
276 | } |
|
|||
277 | Ok(c_res) |
|
|||
278 | } |
|
268 | } | |
279 |
|
269 | |||
280 | /// True if the object is a snapshot |
|
270 | /// True if the object is a snapshot | |
281 | def issnapshot(&self, *args, **kw) -> PyResult<bool> { |
|
271 | def issnapshot(&self, *args, **kw) -> PyResult<bool> { | |
282 | let index = self.index(py).borrow(); |
|
272 | let index = self.index(py).borrow(); | |
283 | let result = index |
|
273 | let result = index | |
284 | .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?)) |
|
274 | .is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?)) | |
285 | .map_err(|e| { |
|
275 | .map_err(|e| { | |
286 | PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string()) |
|
276 | PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string()) | |
287 | })?; |
|
277 | })?; | |
288 | let cresult = self.call_cindex(py, "issnapshot", args, kw)?; |
|
278 | let cresult = self.call_cindex(py, "issnapshot", args, kw)?; | |
289 | assert_eq!(result, cresult.extract(py)?); |
|
279 | assert_eq!(result, cresult.extract(py)?); | |
290 | Ok(result) |
|
280 | Ok(result) | |
291 | } |
|
281 | } | |
292 |
|
282 | |||
293 | /// Gather snapshot data in a cache dict |
|
283 | /// Gather snapshot data in a cache dict | |
294 | def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> { |
|
284 | def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> { | |
295 | let index = self.index(py).borrow(); |
|
285 | let index = self.index(py).borrow(); | |
296 | let cache: PyDict = args.get_item(py, 0).extract(py)?; |
|
286 | let cache: PyDict = args.get_item(py, 0).extract(py)?; | |
297 | // this methods operates by setting new values in the cache, |
|
287 | // this methods operates by setting new values in the cache, | |
298 | // hence we will compare results by letting the C implementation |
|
288 | // hence we will compare results by letting the C implementation | |
299 | // operate over a deepcopy of the cache, and finally compare both |
|
289 | // operate over a deepcopy of the cache, and finally compare both | |
300 | // caches. |
|
290 | // caches. | |
301 | let c_cache = PyDict::new(py); |
|
291 | let c_cache = PyDict::new(py); | |
302 | for (k, v) in cache.items(py) { |
|
292 | for (k, v) in cache.items(py) { | |
303 | c_cache.set_item(py, k, PySet::new(py, v)?)?; |
|
293 | c_cache.set_item(py, k, PySet::new(py, v)?)?; | |
304 | } |
|
294 | } | |
305 |
|
295 | |||
306 | let start_rev = UncheckedRevision(args.get_item(py, 1).extract(py)?); |
|
296 | let start_rev = UncheckedRevision(args.get_item(py, 1).extract(py)?); | |
307 | let end_rev = UncheckedRevision(args.get_item(py, 2).extract(py)?); |
|
297 | let end_rev = UncheckedRevision(args.get_item(py, 2).extract(py)?); | |
308 | let mut cache_wrapper = PySnapshotsCache{ py, dict: cache }; |
|
298 | let mut cache_wrapper = PySnapshotsCache{ py, dict: cache }; | |
309 | index.find_snapshots( |
|
299 | index.find_snapshots( | |
310 | start_rev, |
|
300 | start_rev, | |
311 | end_rev, |
|
301 | end_rev, | |
312 | &mut cache_wrapper, |
|
302 | &mut cache_wrapper, | |
313 | ).map_err(|_| revlog_error(py))?; |
|
303 | ).map_err(|_| revlog_error(py))?; | |
314 |
|
304 | |||
315 | let c_args = PyTuple::new( |
|
305 | let c_args = PyTuple::new( | |
316 | py, |
|
306 | py, | |
317 | &[ |
|
307 | &[ | |
318 | c_cache.clone_ref(py).into_object(), |
|
308 | c_cache.clone_ref(py).into_object(), | |
319 | args.get_item(py, 1), |
|
309 | args.get_item(py, 1), | |
320 | args.get_item(py, 2) |
|
310 | args.get_item(py, 2) | |
321 | ] |
|
311 | ] | |
322 | ); |
|
312 | ); | |
323 | self.call_cindex(py, "findsnapshots", &c_args, kw)?; |
|
313 | self.call_cindex(py, "findsnapshots", &c_args, kw)?; | |
324 | assert_py_eq(py, "findsnapshots cache", |
|
314 | assert_py_eq(py, "findsnapshots cache", | |
325 | &cache_wrapper.into_object(), |
|
315 | &cache_wrapper.into_object(), | |
326 | &c_cache.into_object())?; |
|
316 | &c_cache.into_object())?; | |
327 | Ok(py.None()) |
|
317 | Ok(py.None()) | |
328 | } |
|
318 | } | |
329 |
|
319 | |||
330 | /// determine revisions with deltas to reconstruct fulltext |
|
320 | /// determine revisions with deltas to reconstruct fulltext | |
331 | def deltachain(&self, *args, **kw) -> PyResult<PyObject> { |
|
321 | def deltachain(&self, *args, **kw) -> PyResult<PyObject> { | |
332 | let index = self.index(py).borrow(); |
|
322 | let index = self.index(py).borrow(); | |
333 | let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into(); |
|
323 | let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into(); | |
334 | let stop_rev = |
|
324 | let stop_rev = | |
335 | args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?; |
|
325 | args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?; | |
336 | let rev = index.check_revision(rev).ok_or_else(|| { |
|
326 | let rev = index.check_revision(rev).ok_or_else(|| { | |
337 | nodemap_error(py, NodeMapError::RevisionNotInIndex(rev)) |
|
327 | nodemap_error(py, NodeMapError::RevisionNotInIndex(rev)) | |
338 | })?; |
|
328 | })?; | |
339 | let stop_rev = if let Some(stop_rev) = stop_rev { |
|
329 | let stop_rev = if let Some(stop_rev) = stop_rev { | |
340 | let stop_rev = UncheckedRevision(stop_rev); |
|
330 | let stop_rev = UncheckedRevision(stop_rev); | |
341 | Some(index.check_revision(stop_rev).ok_or_else(|| { |
|
331 | Some(index.check_revision(stop_rev).ok_or_else(|| { | |
342 | nodemap_error(py, NodeMapError::RevisionNotInIndex(stop_rev)) |
|
332 | nodemap_error(py, NodeMapError::RevisionNotInIndex(stop_rev)) | |
343 | })?) |
|
333 | })?) | |
344 | } else {None}; |
|
334 | } else {None}; | |
345 | let (chain, stopped) = index.delta_chain(rev, stop_rev).map_err(|e| { |
|
335 | let (chain, stopped) = index.delta_chain(rev, stop_rev).map_err(|e| { | |
346 | PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string()) |
|
336 | PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string()) | |
347 | })?; |
|
337 | })?; | |
348 |
|
338 | |||
349 | let cresult = self.call_cindex(py, "deltachain", args, kw)?; |
|
339 | let cresult = self.call_cindex(py, "deltachain", args, kw)?; | |
350 | let cchain: Vec<BaseRevision> = |
|
340 | let cchain: Vec<BaseRevision> = | |
351 | cresult.get_item(py, 0)?.extract::<Vec<BaseRevision>>(py)?; |
|
341 | cresult.get_item(py, 0)?.extract::<Vec<BaseRevision>>(py)?; | |
352 | let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect(); |
|
342 | let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect(); | |
353 | assert_eq!(chain, cchain); |
|
343 | assert_eq!(chain, cchain); | |
354 | assert_eq!(stopped, cresult.get_item(py, 1)?.extract(py)?); |
|
344 | assert_eq!(stopped, cresult.get_item(py, 1)?.extract(py)?); | |
355 |
|
345 | |||
356 | Ok( |
|
346 | Ok( | |
357 | PyTuple::new( |
|
347 | PyTuple::new( | |
358 | py, |
|
348 | py, | |
359 | &[ |
|
349 | &[ | |
360 | chain.into_py_object(py).into_object(), |
|
350 | chain.into_py_object(py).into_object(), | |
361 | stopped.into_py_object(py).into_object() |
|
351 | stopped.into_py_object(py).into_object() | |
362 | ] |
|
352 | ] | |
363 | ).into_object() |
|
353 | ).into_object() | |
364 | ) |
|
354 | ) | |
365 |
|
355 | |||
366 | } |
|
356 | } | |
367 |
|
357 | |||
368 | /// slice planned chunk read to reach a density threshold |
|
358 | /// slice planned chunk read to reach a density threshold | |
369 | def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> { |
|
359 | def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> { | |
370 | self.call_cindex(py, "slicechunktodensity", args, kw) |
|
360 | self.call_cindex(py, "slicechunktodensity", args, kw) | |
371 | } |
|
361 | } | |
372 |
|
362 | |||
373 | /// stats for the index |
|
363 | /// stats for the index | |
374 | def stats(&self, *args, **kw) -> PyResult<PyObject> { |
|
364 | def stats(&self, *args, **kw) -> PyResult<PyObject> { | |
375 | self.call_cindex(py, "stats", args, kw) |
|
365 | self.call_cindex(py, "stats", args, kw) | |
376 | } |
|
366 | } | |
377 |
|
367 | |||
378 | // index_sequence_methods and index_mapping_methods. |
|
368 | // index_sequence_methods and index_mapping_methods. | |
379 | // |
|
369 | // | |
380 | // Since we call back through the high level Python API, |
|
370 | // Since we call back through the high level Python API, | |
381 | // there's no point making a distinction between index_get |
|
371 | // there's no point making a distinction between index_get | |
382 | // and index_getitem. |
|
372 | // and index_getitem. | |
383 | // gracinet 2023: this above is no longer true for the pure Rust impl |
|
373 | // gracinet 2023: this above is no longer true for the pure Rust impl | |
384 |
|
374 | |||
385 | def __len__(&self) -> PyResult<usize> { |
|
375 | def __len__(&self) -> PyResult<usize> { | |
386 | self.len(py) |
|
376 | self.len(py) | |
387 | } |
|
377 | } | |
388 |
|
378 | |||
389 | def __getitem__(&self, key: PyObject) -> PyResult<PyObject> { |
|
379 | def __getitem__(&self, key: PyObject) -> PyResult<PyObject> { | |
390 | let rust_res = self.inner_getitem(py, key.clone_ref(py))?; |
|
380 | let rust_res = self.inner_getitem(py, key.clone_ref(py))?; | |
391 |
|
381 | |||
392 | // this conversion seems needless, but that's actually because |
|
382 | // this conversion seems needless, but that's actually because | |
393 | // `index_getitem` does not handle conversion from PyLong, |
|
383 | // `index_getitem` does not handle conversion from PyLong, | |
394 | // which expressions such as [e for e in index] internally use. |
|
384 | // which expressions such as [e for e in index] internally use. | |
395 | // Note that we don't seem to have a direct way to call |
|
385 | // Note that we don't seem to have a direct way to call | |
396 | // PySequence_GetItem (does the job), which would possibly be better |
|
386 | // PySequence_GetItem (does the job), which would possibly be better | |
397 | // for performance |
|
387 | // for performance | |
398 | // gracinet 2023: the above comment can be removed when we use |
|
388 | // gracinet 2023: the above comment can be removed when we use | |
399 | // the pure Rust impl only. Note also that `key` can be a binary |
|
389 | // the pure Rust impl only. Note also that `key` can be a binary | |
400 | // node id. |
|
390 | // node id. | |
401 | let c_key = match key.extract::<BaseRevision>(py) { |
|
391 | let c_key = match key.extract::<BaseRevision>(py) { | |
402 | Ok(rev) => rev.to_py_object(py).into_object(), |
|
392 | Ok(rev) => rev.to_py_object(py).into_object(), | |
403 | Err(_) => key, |
|
393 | Err(_) => key, | |
404 | }; |
|
394 | }; | |
405 | let c_res = self.cindex(py).borrow().inner().get_item(py, c_key)?; |
|
395 | let c_res = self.cindex(py).borrow().inner().get_item(py, c_key)?; | |
406 |
|
396 | |||
407 | assert_py_eq(py, "__getitem__", &rust_res, &c_res)?; |
|
397 | assert_py_eq(py, "__getitem__", &rust_res, &c_res)?; | |
408 | Ok(rust_res) |
|
398 | Ok(rust_res) | |
409 | } |
|
399 | } | |
410 |
|
400 | |||
411 | def __contains__(&self, item: PyObject) -> PyResult<bool> { |
|
401 | def __contains__(&self, item: PyObject) -> PyResult<bool> { | |
412 | // ObjectProtocol does not seem to provide contains(), so |
|
402 | // ObjectProtocol does not seem to provide contains(), so | |
413 | // this is an equivalent implementation of the index_contains() |
|
403 | // this is an equivalent implementation of the index_contains() | |
414 | // defined in revlog.c |
|
404 | // defined in revlog.c | |
415 | let cindex = self.cindex(py).borrow(); |
|
405 | let cindex = self.cindex(py).borrow(); | |
416 | match item.extract::<i32>(py) { |
|
406 | match item.extract::<i32>(py) { | |
417 | Ok(rev) => { |
|
407 | Ok(rev) => { | |
418 | Ok(rev >= -1 && rev < self.len(py)? as BaseRevision) |
|
408 | Ok(rev >= -1 && rev < self.len(py)? as BaseRevision) | |
419 | } |
|
409 | } | |
420 | Err(_) => { |
|
410 | Err(_) => { | |
421 | let item_bytes: PyBytes = item.extract(py)?; |
|
411 | let item_bytes: PyBytes = item.extract(py)?; | |
422 | let rust_res = self.has_node(py, item_bytes)?; |
|
412 | let rust_res = self.has_node(py, item_bytes)?; | |
423 |
|
413 | |||
424 | let c_res = cindex.inner().call_method( |
|
414 | let c_res = cindex.inner().call_method( | |
425 | py, |
|
415 | py, | |
426 | "has_node", |
|
416 | "has_node", | |
427 | PyTuple::new(py, &[item.clone_ref(py)]), |
|
417 | PyTuple::new(py, &[item.clone_ref(py)]), | |
428 | None)? |
|
418 | None)? | |
429 | .extract(py)?; |
|
419 | .extract(py)?; | |
430 |
|
420 | |||
431 | assert_eq!(rust_res, c_res); |
|
421 | assert_eq!(rust_res, c_res); | |
432 | Ok(rust_res) |
|
422 | Ok(rust_res) | |
433 | } |
|
423 | } | |
434 | } |
|
424 | } | |
435 | } |
|
425 | } | |
436 |
|
426 | |||
437 | def nodemap_data_all(&self) -> PyResult<PyBytes> { |
|
427 | def nodemap_data_all(&self) -> PyResult<PyBytes> { | |
438 | self.inner_nodemap_data_all(py) |
|
428 | self.inner_nodemap_data_all(py) | |
439 | } |
|
429 | } | |
440 |
|
430 | |||
441 | def nodemap_data_incremental(&self) -> PyResult<PyObject> { |
|
431 | def nodemap_data_incremental(&self) -> PyResult<PyObject> { | |
442 | self.inner_nodemap_data_incremental(py) |
|
432 | self.inner_nodemap_data_incremental(py) | |
443 | } |
|
433 | } | |
444 | def update_nodemap_data( |
|
434 | def update_nodemap_data( | |
445 | &self, |
|
435 | &self, | |
446 | docket: PyObject, |
|
436 | docket: PyObject, | |
447 | nm_data: PyObject |
|
437 | nm_data: PyObject | |
448 | ) -> PyResult<PyObject> { |
|
438 | ) -> PyResult<PyObject> { | |
449 | self.inner_update_nodemap_data(py, docket, nm_data) |
|
439 | self.inner_update_nodemap_data(py, docket, nm_data) | |
450 | } |
|
440 | } | |
451 |
|
441 | |||
452 | @property |
|
442 | @property | |
453 | def entry_size(&self) -> PyResult<PyInt> { |
|
443 | def entry_size(&self) -> PyResult<PyInt> { | |
454 | self.cindex(py).borrow().inner().getattr(py, "entry_size")?.extract::<PyInt>(py) |
|
444 | self.cindex(py).borrow().inner().getattr(py, "entry_size")?.extract::<PyInt>(py) | |
455 | } |
|
445 | } | |
456 |
|
446 | |||
457 | @property |
|
447 | @property | |
458 | def rust_ext_compat(&self) -> PyResult<PyInt> { |
|
448 | def rust_ext_compat(&self) -> PyResult<PyInt> { | |
459 | self.cindex(py).borrow().inner().getattr(py, "rust_ext_compat")?.extract::<PyInt>(py) |
|
449 | self.cindex(py).borrow().inner().getattr(py, "rust_ext_compat")?.extract::<PyInt>(py) | |
460 | } |
|
450 | } | |
461 |
|
451 | |||
462 | }); |
|
452 | }); | |
463 |
|
453 | |||
464 | /// Take a (potentially) mmap'ed buffer, and return the underlying Python |
|
454 | /// Take a (potentially) mmap'ed buffer, and return the underlying Python | |
465 | /// buffer along with the Rust slice into said buffer. We need to keep the |
|
455 | /// buffer along with the Rust slice into said buffer. We need to keep the | |
466 | /// Python buffer around, otherwise we'd get a dangling pointer once the buffer |
|
456 | /// Python buffer around, otherwise we'd get a dangling pointer once the buffer | |
467 | /// is freed from Python's side. |
|
457 | /// is freed from Python's side. | |
468 | /// |
|
458 | /// | |
469 | /// # Safety |
|
459 | /// # Safety | |
470 | /// |
|
460 | /// | |
471 | /// The caller must make sure that the buffer is kept around for at least as |
|
461 | /// The caller must make sure that the buffer is kept around for at least as | |
472 | /// long as the slice. |
|
462 | /// long as the slice. | |
473 | #[deny(unsafe_op_in_unsafe_fn)] |
|
463 | #[deny(unsafe_op_in_unsafe_fn)] | |
474 | unsafe fn mmap_keeparound( |
|
464 | unsafe fn mmap_keeparound( | |
475 | py: Python, |
|
465 | py: Python, | |
476 | data: PyObject, |
|
466 | data: PyObject, | |
477 | ) -> PyResult<( |
|
467 | ) -> PyResult<( | |
478 | PyBuffer, |
|
468 | PyBuffer, | |
479 | Box<dyn std::ops::Deref<Target = [u8]> + Send + 'static>, |
|
469 | Box<dyn std::ops::Deref<Target = [u8]> + Send + 'static>, | |
480 | )> { |
|
470 | )> { | |
481 | let buf = PyBuffer::get(py, &data)?; |
|
471 | let buf = PyBuffer::get(py, &data)?; | |
482 | let len = buf.item_count(); |
|
472 | let len = buf.item_count(); | |
483 |
|
473 | |||
484 | // Build a slice from the mmap'ed buffer data |
|
474 | // Build a slice from the mmap'ed buffer data | |
485 | let cbuf = buf.buf_ptr(); |
|
475 | let cbuf = buf.buf_ptr(); | |
486 | let bytes = if std::mem::size_of::<u8>() == buf.item_size() |
|
476 | let bytes = if std::mem::size_of::<u8>() == buf.item_size() | |
487 | && buf.is_c_contiguous() |
|
477 | && buf.is_c_contiguous() | |
488 | && u8::is_compatible_format(buf.format()) |
|
478 | && u8::is_compatible_format(buf.format()) | |
489 | { |
|
479 | { | |
490 | unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) } |
|
480 | unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) } | |
491 | } else { |
|
481 | } else { | |
492 | return Err(PyErr::new::<ValueError, _>( |
|
482 | return Err(PyErr::new::<ValueError, _>( | |
493 | py, |
|
483 | py, | |
494 | "Nodemap data buffer has an invalid memory representation" |
|
484 | "Nodemap data buffer has an invalid memory representation" | |
495 | .to_string(), |
|
485 | .to_string(), | |
496 | )); |
|
486 | )); | |
497 | }; |
|
487 | }; | |
498 |
|
488 | |||
499 | Ok((buf, Box::new(bytes))) |
|
489 | Ok((buf, Box::new(bytes))) | |
500 | } |
|
490 | } | |
501 |
|
491 | |||
502 | fn py_tuple_to_revision_data_params( |
|
492 | fn py_tuple_to_revision_data_params( | |
503 | py: Python, |
|
493 | py: Python, | |
504 | tuple: PyTuple, |
|
494 | tuple: PyTuple, | |
505 | ) -> PyResult<RevisionDataParams> { |
|
495 | ) -> PyResult<RevisionDataParams> { | |
506 | if tuple.len(py) < 8 { |
|
496 | if tuple.len(py) < 8 { | |
507 | // this is better than the panic promised by tup.get_item() |
|
497 | // this is better than the panic promised by tup.get_item() | |
508 | return Err(PyErr::new::<IndexError, _>( |
|
498 | return Err(PyErr::new::<IndexError, _>( | |
509 | py, |
|
499 | py, | |
510 | "tuple index out of range", |
|
500 | "tuple index out of range", | |
511 | )); |
|
501 | )); | |
512 | } |
|
502 | } | |
513 | let offset_or_flags: u64 = tuple.get_item(py, 0).extract(py)?; |
|
503 | let offset_or_flags: u64 = tuple.get_item(py, 0).extract(py)?; | |
514 | let node_id = tuple |
|
504 | let node_id = tuple | |
515 | .get_item(py, 7) |
|
505 | .get_item(py, 7) | |
516 | .extract::<PyBytes>(py)? |
|
506 | .extract::<PyBytes>(py)? | |
517 | .data(py) |
|
507 | .data(py) | |
518 | .try_into() |
|
508 | .try_into() | |
519 | .unwrap(); |
|
509 | .unwrap(); | |
520 | let flags = (offset_or_flags & 0xFFFF) as u16; |
|
510 | let flags = (offset_or_flags & 0xFFFF) as u16; | |
521 | let data_offset = offset_or_flags >> 16; |
|
511 | let data_offset = offset_or_flags >> 16; | |
522 | Ok(RevisionDataParams { |
|
512 | Ok(RevisionDataParams { | |
523 | flags, |
|
513 | flags, | |
524 | data_offset, |
|
514 | data_offset, | |
525 | data_compressed_length: tuple.get_item(py, 1).extract(py)?, |
|
515 | data_compressed_length: tuple.get_item(py, 1).extract(py)?, | |
526 | data_uncompressed_length: tuple.get_item(py, 2).extract(py)?, |
|
516 | data_uncompressed_length: tuple.get_item(py, 2).extract(py)?, | |
527 | data_delta_base: tuple.get_item(py, 3).extract(py)?, |
|
517 | data_delta_base: tuple.get_item(py, 3).extract(py)?, | |
528 | link_rev: tuple.get_item(py, 4).extract(py)?, |
|
518 | link_rev: tuple.get_item(py, 4).extract(py)?, | |
529 | parent_rev_1: tuple.get_item(py, 5).extract(py)?, |
|
519 | parent_rev_1: tuple.get_item(py, 5).extract(py)?, | |
530 | parent_rev_2: tuple.get_item(py, 6).extract(py)?, |
|
520 | parent_rev_2: tuple.get_item(py, 6).extract(py)?, | |
531 | node_id, |
|
521 | node_id, | |
532 | ..Default::default() |
|
522 | ..Default::default() | |
533 | }) |
|
523 | }) | |
534 | } |
|
524 | } | |
535 | fn revision_data_params_to_py_tuple( |
|
525 | fn revision_data_params_to_py_tuple( | |
536 | py: Python, |
|
526 | py: Python, | |
537 | params: RevisionDataParams, |
|
527 | params: RevisionDataParams, | |
538 | ) -> PyTuple { |
|
528 | ) -> PyTuple { | |
539 | PyTuple::new( |
|
529 | PyTuple::new( | |
540 | py, |
|
530 | py, | |
541 | &[ |
|
531 | &[ | |
542 | params.data_offset.into_py_object(py).into_object(), |
|
532 | params.data_offset.into_py_object(py).into_object(), | |
543 | params |
|
533 | params | |
544 | .data_compressed_length |
|
534 | .data_compressed_length | |
545 | .into_py_object(py) |
|
535 | .into_py_object(py) | |
546 | .into_object(), |
|
536 | .into_object(), | |
547 | params |
|
537 | params | |
548 | .data_uncompressed_length |
|
538 | .data_uncompressed_length | |
549 | .into_py_object(py) |
|
539 | .into_py_object(py) | |
550 | .into_object(), |
|
540 | .into_object(), | |
551 | params.data_delta_base.into_py_object(py).into_object(), |
|
541 | params.data_delta_base.into_py_object(py).into_object(), | |
552 | params.link_rev.into_py_object(py).into_object(), |
|
542 | params.link_rev.into_py_object(py).into_object(), | |
553 | params.parent_rev_1.into_py_object(py).into_object(), |
|
543 | params.parent_rev_1.into_py_object(py).into_object(), | |
554 | params.parent_rev_2.into_py_object(py).into_object(), |
|
544 | params.parent_rev_2.into_py_object(py).into_object(), | |
555 | PyBytes::new(py, ¶ms.node_id) |
|
545 | PyBytes::new(py, ¶ms.node_id) | |
556 | .into_py_object(py) |
|
546 | .into_py_object(py) | |
557 | .into_object(), |
|
547 | .into_object(), | |
558 | params._sidedata_offset.into_py_object(py).into_object(), |
|
548 | params._sidedata_offset.into_py_object(py).into_object(), | |
559 | params |
|
549 | params | |
560 | ._sidedata_compressed_length |
|
550 | ._sidedata_compressed_length | |
561 | .into_py_object(py) |
|
551 | .into_py_object(py) | |
562 | .into_object(), |
|
552 | .into_object(), | |
563 | params |
|
553 | params | |
564 | .data_compression_mode |
|
554 | .data_compression_mode | |
565 | .into_py_object(py) |
|
555 | .into_py_object(py) | |
566 | .into_object(), |
|
556 | .into_object(), | |
567 | params |
|
557 | params | |
568 | ._sidedata_compression_mode |
|
558 | ._sidedata_compression_mode | |
569 | .into_py_object(py) |
|
559 | .into_py_object(py) | |
570 | .into_object(), |
|
560 | .into_object(), | |
571 | params._rank.into_py_object(py).into_object(), |
|
561 | params._rank.into_py_object(py).into_object(), | |
572 | ], |
|
562 | ], | |
573 | ) |
|
563 | ) | |
574 | } |
|
564 | } | |
575 |
|
565 | |||
576 | struct PySnapshotsCache<'p> { |
|
566 | struct PySnapshotsCache<'p> { | |
577 | py: Python<'p>, |
|
567 | py: Python<'p>, | |
578 | dict: PyDict, |
|
568 | dict: PyDict, | |
579 | } |
|
569 | } | |
580 |
|
570 | |||
581 | impl<'p> PySnapshotsCache<'p> { |
|
571 | impl<'p> PySnapshotsCache<'p> { | |
582 | fn into_object(self) -> PyObject { |
|
572 | fn into_object(self) -> PyObject { | |
583 | self.dict.into_object() |
|
573 | self.dict.into_object() | |
584 | } |
|
574 | } | |
585 | } |
|
575 | } | |
586 |
|
576 | |||
587 | impl<'p> SnapshotsCache for PySnapshotsCache<'p> { |
|
577 | impl<'p> SnapshotsCache for PySnapshotsCache<'p> { | |
588 | fn insert_for( |
|
578 | fn insert_for( | |
589 | &mut self, |
|
579 | &mut self, | |
590 | rev: BaseRevision, |
|
580 | rev: BaseRevision, | |
591 | value: BaseRevision, |
|
581 | value: BaseRevision, | |
592 | ) -> Result<(), RevlogError> { |
|
582 | ) -> Result<(), RevlogError> { | |
593 | let pyvalue = value.into_py_object(self.py).into_object(); |
|
583 | let pyvalue = value.into_py_object(self.py).into_object(); | |
594 | match self.dict.get_item(self.py, rev) { |
|
584 | match self.dict.get_item(self.py, rev) { | |
595 | Some(obj) => obj |
|
585 | Some(obj) => obj | |
596 | .extract::<PySet>(self.py) |
|
586 | .extract::<PySet>(self.py) | |
597 | .and_then(|set| set.add(self.py, pyvalue)), |
|
587 | .and_then(|set| set.add(self.py, pyvalue)), | |
598 | None => PySet::new(self.py, vec![pyvalue]) |
|
588 | None => PySet::new(self.py, vec![pyvalue]) | |
599 | .and_then(|set| self.dict.set_item(self.py, rev, set)), |
|
589 | .and_then(|set| self.dict.set_item(self.py, rev, set)), | |
600 | } |
|
590 | } | |
601 | .map_err(|_| { |
|
591 | .map_err(|_| { | |
602 | RevlogError::Other(HgError::unsupported( |
|
592 | RevlogError::Other(HgError::unsupported( | |
603 | "Error in Python caches handling", |
|
593 | "Error in Python caches handling", | |
604 | )) |
|
594 | )) | |
605 | }) |
|
595 | }) | |
606 | } |
|
596 | } | |
607 | } |
|
597 | } | |
608 |
|
598 | |||
609 | impl MixedIndex { |
|
599 | impl MixedIndex { | |
610 | fn new( |
|
600 | fn new( | |
611 | py: Python, |
|
601 | py: Python, | |
612 | cindex: PyObject, |
|
602 | cindex: PyObject, | |
613 | data: PyObject, |
|
603 | data: PyObject, | |
614 | header: u32, |
|
604 | header: u32, | |
615 | ) -> PyResult<MixedIndex> { |
|
605 | ) -> PyResult<MixedIndex> { | |
616 | // Safety: we keep the buffer around inside the class as `index_mmap` |
|
606 | // Safety: we keep the buffer around inside the class as `index_mmap` | |
617 | let (buf, bytes) = unsafe { mmap_keeparound(py, data)? }; |
|
607 | let (buf, bytes) = unsafe { mmap_keeparound(py, data)? }; | |
618 |
|
608 | |||
619 | Self::create_instance( |
|
609 | Self::create_instance( | |
620 | py, |
|
610 | py, | |
621 | RefCell::new(cindex::Index::new(py, cindex)?), |
|
611 | RefCell::new(cindex::Index::new(py, cindex)?), | |
622 | RefCell::new( |
|
612 | RefCell::new( | |
623 | hg::index::Index::new( |
|
613 | hg::index::Index::new( | |
624 | bytes, |
|
614 | bytes, | |
625 | IndexHeader::parse(&header.to_be_bytes()) |
|
615 | IndexHeader::parse(&header.to_be_bytes()) | |
626 | .expect("default header is broken") |
|
616 | .expect("default header is broken") | |
627 | .unwrap(), |
|
617 | .unwrap(), | |
628 | ) |
|
618 | ) | |
629 | .unwrap(), |
|
619 | .unwrap(), | |
630 | ), |
|
620 | ), | |
631 | RefCell::new(None), |
|
621 | RefCell::new(None), | |
632 | RefCell::new(None), |
|
622 | RefCell::new(None), | |
633 | RefCell::new(None), |
|
623 | RefCell::new(None), | |
634 | RefCell::new(Some(buf)), |
|
624 | RefCell::new(Some(buf)), | |
635 | ) |
|
625 | ) | |
636 | } |
|
626 | } | |
637 |
|
627 | |||
638 | fn len(&self, py: Python) -> PyResult<usize> { |
|
628 | fn len(&self, py: Python) -> PyResult<usize> { | |
639 | let rust_index_len = self.index(py).borrow().len(); |
|
629 | let rust_index_len = self.index(py).borrow().len(); | |
640 | let cindex_len = self.cindex(py).borrow().inner().len(py)?; |
|
630 | let cindex_len = self.cindex(py).borrow().inner().len(py)?; | |
641 | assert_eq!(rust_index_len, cindex_len); |
|
631 | assert_eq!(rust_index_len, cindex_len); | |
642 | Ok(cindex_len) |
|
632 | Ok(cindex_len) | |
643 | } |
|
633 | } | |
644 |
|
634 | |||
645 | /// This is scaffolding at this point, but it could also become |
|
635 | /// This is scaffolding at this point, but it could also become | |
646 | /// a way to start a persistent nodemap or perform a |
|
636 | /// a way to start a persistent nodemap or perform a | |
647 | /// vacuum / repack operation |
|
637 | /// vacuum / repack operation | |
648 | fn fill_nodemap( |
|
638 | fn fill_nodemap( | |
649 | &self, |
|
639 | &self, | |
650 | py: Python, |
|
640 | py: Python, | |
651 | nt: &mut NodeTree, |
|
641 | nt: &mut NodeTree, | |
652 | ) -> PyResult<PyObject> { |
|
642 | ) -> PyResult<PyObject> { | |
653 | let index = self.index(py).borrow(); |
|
643 | let index = self.index(py).borrow(); | |
654 | for r in 0..self.len(py)? { |
|
644 | for r in 0..self.len(py)? { | |
655 | let rev = Revision(r as BaseRevision); |
|
645 | let rev = Revision(r as BaseRevision); | |
656 | // in this case node() won't ever return None |
|
646 | // in this case node() won't ever return None | |
657 | nt.insert(&*index, index.node(rev).unwrap(), rev) |
|
647 | nt.insert(&*index, index.node(rev).unwrap(), rev) | |
658 | .map_err(|e| nodemap_error(py, e))? |
|
648 | .map_err(|e| nodemap_error(py, e))? | |
659 | } |
|
649 | } | |
660 | Ok(py.None()) |
|
650 | Ok(py.None()) | |
661 | } |
|
651 | } | |
662 |
|
652 | |||
663 | fn get_nodetree<'a>( |
|
653 | fn get_nodetree<'a>( | |
664 | &'a self, |
|
654 | &'a self, | |
665 | py: Python<'a>, |
|
655 | py: Python<'a>, | |
666 | ) -> PyResult<&'a RefCell<Option<NodeTree>>> { |
|
656 | ) -> PyResult<&'a RefCell<Option<NodeTree>>> { | |
667 | if self.nt(py).borrow().is_none() { |
|
657 | if self.nt(py).borrow().is_none() { | |
668 | let readonly = Box::<Vec<_>>::default(); |
|
658 | let readonly = Box::<Vec<_>>::default(); | |
669 | let mut nt = NodeTree::load_bytes(readonly, 0); |
|
659 | let mut nt = NodeTree::load_bytes(readonly, 0); | |
670 | self.fill_nodemap(py, &mut nt)?; |
|
660 | self.fill_nodemap(py, &mut nt)?; | |
671 | self.nt(py).borrow_mut().replace(nt); |
|
661 | self.nt(py).borrow_mut().replace(nt); | |
672 | } |
|
662 | } | |
673 | Ok(self.nt(py)) |
|
663 | Ok(self.nt(py)) | |
674 | } |
|
664 | } | |
675 |
|
665 | |||
676 | /// forward a method call to the underlying C index |
|
666 | /// forward a method call to the underlying C index | |
677 | fn call_cindex( |
|
667 | fn call_cindex( | |
678 | &self, |
|
668 | &self, | |
679 | py: Python, |
|
669 | py: Python, | |
680 | name: &str, |
|
670 | name: &str, | |
681 | args: &PyTuple, |
|
671 | args: &PyTuple, | |
682 | kwargs: Option<&PyDict>, |
|
672 | kwargs: Option<&PyDict>, | |
683 | ) -> PyResult<PyObject> { |
|
673 | ) -> PyResult<PyObject> { | |
684 | self.cindex(py) |
|
674 | self.cindex(py) | |
685 | .borrow() |
|
675 | .borrow() | |
686 | .inner() |
|
676 | .inner() | |
687 | .call_method(py, name, args, kwargs) |
|
677 | .call_method(py, name, args, kwargs) | |
688 | } |
|
678 | } | |
689 |
|
679 | |||
690 | pub fn clone_cindex(&self, py: Python) -> cindex::Index { |
|
680 | pub fn clone_cindex(&self, py: Python) -> cindex::Index { | |
691 | self.cindex(py).borrow().clone_ref(py) |
|
681 | self.cindex(py).borrow().clone_ref(py) | |
692 | } |
|
682 | } | |
693 |
|
683 | |||
694 | /// Returns the full nodemap bytes to be written as-is to disk |
|
684 | /// Returns the full nodemap bytes to be written as-is to disk | |
695 | fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> { |
|
685 | fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> { | |
696 | let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap(); |
|
686 | let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap(); | |
697 | let (readonly, bytes) = nodemap.into_readonly_and_added_bytes(); |
|
687 | let (readonly, bytes) = nodemap.into_readonly_and_added_bytes(); | |
698 |
|
688 | |||
699 | // If there's anything readonly, we need to build the data again from |
|
689 | // If there's anything readonly, we need to build the data again from | |
700 | // scratch |
|
690 | // scratch | |
701 | let bytes = if readonly.len() > 0 { |
|
691 | let bytes = if readonly.len() > 0 { | |
702 | let mut nt = NodeTree::load_bytes(Box::<Vec<_>>::default(), 0); |
|
692 | let mut nt = NodeTree::load_bytes(Box::<Vec<_>>::default(), 0); | |
703 | self.fill_nodemap(py, &mut nt)?; |
|
693 | self.fill_nodemap(py, &mut nt)?; | |
704 |
|
694 | |||
705 | let (readonly, bytes) = nt.into_readonly_and_added_bytes(); |
|
695 | let (readonly, bytes) = nt.into_readonly_and_added_bytes(); | |
706 | assert_eq!(readonly.len(), 0); |
|
696 | assert_eq!(readonly.len(), 0); | |
707 |
|
697 | |||
708 | bytes |
|
698 | bytes | |
709 | } else { |
|
699 | } else { | |
710 | bytes |
|
700 | bytes | |
711 | }; |
|
701 | }; | |
712 |
|
702 | |||
713 | let bytes = PyBytes::new(py, &bytes); |
|
703 | let bytes = PyBytes::new(py, &bytes); | |
714 | Ok(bytes) |
|
704 | Ok(bytes) | |
715 | } |
|
705 | } | |
716 |
|
706 | |||
717 | /// Returns the last saved docket along with the size of any changed data |
|
707 | /// Returns the last saved docket along with the size of any changed data | |
718 | /// (in number of blocks), and said data as bytes. |
|
708 | /// (in number of blocks), and said data as bytes. | |
719 | fn inner_nodemap_data_incremental( |
|
709 | fn inner_nodemap_data_incremental( | |
720 | &self, |
|
710 | &self, | |
721 | py: Python, |
|
711 | py: Python, | |
722 | ) -> PyResult<PyObject> { |
|
712 | ) -> PyResult<PyObject> { | |
723 | let docket = self.docket(py).borrow(); |
|
713 | let docket = self.docket(py).borrow(); | |
724 | let docket = match docket.as_ref() { |
|
714 | let docket = match docket.as_ref() { | |
725 | Some(d) => d, |
|
715 | Some(d) => d, | |
726 | None => return Ok(py.None()), |
|
716 | None => return Ok(py.None()), | |
727 | }; |
|
717 | }; | |
728 |
|
718 | |||
729 | let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap(); |
|
719 | let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap(); | |
730 | let masked_blocks = node_tree.masked_readonly_blocks(); |
|
720 | let masked_blocks = node_tree.masked_readonly_blocks(); | |
731 | let (_, data) = node_tree.into_readonly_and_added_bytes(); |
|
721 | let (_, data) = node_tree.into_readonly_and_added_bytes(); | |
732 | let changed = masked_blocks * std::mem::size_of::<Block>(); |
|
722 | let changed = masked_blocks * std::mem::size_of::<Block>(); | |
733 |
|
723 | |||
734 | Ok((docket, changed, PyBytes::new(py, &data)) |
|
724 | Ok((docket, changed, PyBytes::new(py, &data)) | |
735 | .to_py_object(py) |
|
725 | .to_py_object(py) | |
736 | .into_object()) |
|
726 | .into_object()) | |
737 | } |
|
727 | } | |
738 |
|
728 | |||
739 | /// Update the nodemap from the new (mmaped) data. |
|
729 | /// Update the nodemap from the new (mmaped) data. | |
740 | /// The docket is kept as a reference for later incremental calls. |
|
730 | /// The docket is kept as a reference for later incremental calls. | |
741 | fn inner_update_nodemap_data( |
|
731 | fn inner_update_nodemap_data( | |
742 | &self, |
|
732 | &self, | |
743 | py: Python, |
|
733 | py: Python, | |
744 | docket: PyObject, |
|
734 | docket: PyObject, | |
745 | nm_data: PyObject, |
|
735 | nm_data: PyObject, | |
746 | ) -> PyResult<PyObject> { |
|
736 | ) -> PyResult<PyObject> { | |
747 | // Safety: we keep the buffer around inside the class as `nodemap_mmap` |
|
737 | // Safety: we keep the buffer around inside the class as `nodemap_mmap` | |
748 | let (buf, bytes) = unsafe { mmap_keeparound(py, nm_data)? }; |
|
738 | let (buf, bytes) = unsafe { mmap_keeparound(py, nm_data)? }; | |
749 | let len = buf.item_count(); |
|
739 | let len = buf.item_count(); | |
750 | self.nodemap_mmap(py).borrow_mut().replace(buf); |
|
740 | self.nodemap_mmap(py).borrow_mut().replace(buf); | |
751 |
|
741 | |||
752 | let mut nt = NodeTree::load_bytes(bytes, len); |
|
742 | let mut nt = NodeTree::load_bytes(bytes, len); | |
753 |
|
743 | |||
754 | let data_tip = docket |
|
744 | let data_tip = docket | |
755 | .getattr(py, "tip_rev")? |
|
745 | .getattr(py, "tip_rev")? | |
756 | .extract::<BaseRevision>(py)? |
|
746 | .extract::<BaseRevision>(py)? | |
757 | .into(); |
|
747 | .into(); | |
758 | self.docket(py).borrow_mut().replace(docket.clone_ref(py)); |
|
748 | self.docket(py).borrow_mut().replace(docket.clone_ref(py)); | |
759 | let idx = self.index(py).borrow(); |
|
749 | let idx = self.index(py).borrow(); | |
760 | let data_tip = idx.check_revision(data_tip).ok_or_else(|| { |
|
750 | let data_tip = idx.check_revision(data_tip).ok_or_else(|| { | |
761 | nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip)) |
|
751 | nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip)) | |
762 | })?; |
|
752 | })?; | |
763 | let current_tip = idx.len(); |
|
753 | let current_tip = idx.len(); | |
764 |
|
754 | |||
765 | for r in (data_tip.0 + 1)..current_tip as BaseRevision { |
|
755 | for r in (data_tip.0 + 1)..current_tip as BaseRevision { | |
766 | let rev = Revision(r); |
|
756 | let rev = Revision(r); | |
767 | // in this case node() won't ever return None |
|
757 | // in this case node() won't ever return None | |
768 | nt.insert(&*idx, idx.node(rev).unwrap(), rev) |
|
758 | nt.insert(&*idx, idx.node(rev).unwrap(), rev) | |
769 | .map_err(|e| nodemap_error(py, e))? |
|
759 | .map_err(|e| nodemap_error(py, e))? | |
770 | } |
|
760 | } | |
771 |
|
761 | |||
772 | *self.nt(py).borrow_mut() = Some(nt); |
|
762 | *self.nt(py).borrow_mut() = Some(nt); | |
773 |
|
763 | |||
774 | Ok(py.None()) |
|
764 | Ok(py.None()) | |
775 | } |
|
765 | } | |
776 |
|
766 | |||
777 | fn inner_getitem(&self, py: Python, key: PyObject) -> PyResult<PyObject> { |
|
767 | fn inner_getitem(&self, py: Python, key: PyObject) -> PyResult<PyObject> { | |
778 | let idx = self.index(py).borrow(); |
|
768 | let idx = self.index(py).borrow(); | |
779 | Ok(match key.extract::<BaseRevision>(py) { |
|
769 | Ok(match key.extract::<BaseRevision>(py) { | |
780 | Ok(key_as_int) => { |
|
770 | Ok(key_as_int) => { | |
781 | let entry_params = if key_as_int == NULL_REVISION.0 { |
|
771 | let entry_params = if key_as_int == NULL_REVISION.0 { | |
782 | RevisionDataParams::default() |
|
772 | RevisionDataParams::default() | |
783 | } else { |
|
773 | } else { | |
784 | let rev = UncheckedRevision(key_as_int); |
|
774 | let rev = UncheckedRevision(key_as_int); | |
785 | match idx.entry_as_params(rev) { |
|
775 | match idx.entry_as_params(rev) { | |
786 | Some(e) => e, |
|
776 | Some(e) => e, | |
787 | None => { |
|
777 | None => { | |
788 | return Err(PyErr::new::<IndexError, _>( |
|
778 | return Err(PyErr::new::<IndexError, _>( | |
789 | py, |
|
779 | py, | |
790 | "revlog index out of range", |
|
780 | "revlog index out of range", | |
791 | )); |
|
781 | )); | |
792 | } |
|
782 | } | |
793 | } |
|
783 | } | |
794 | }; |
|
784 | }; | |
795 | revision_data_params_to_py_tuple(py, entry_params) |
|
785 | revision_data_params_to_py_tuple(py, entry_params) | |
796 | .into_object() |
|
786 | .into_object() | |
797 | } |
|
787 | } | |
798 | _ => self.get_rev(py, key.extract::<PyBytes>(py)?)?.map_or_else( |
|
788 | _ => self.get_rev(py, key.extract::<PyBytes>(py)?)?.map_or_else( | |
799 | || py.None(), |
|
789 | || py.None(), | |
800 | |py_rev| py_rev.into_py_object(py).into_object(), |
|
790 | |py_rev| py_rev.into_py_object(py).into_object(), | |
801 | ), |
|
791 | ), | |
802 | }) |
|
792 | }) | |
803 | } |
|
793 | } | |
804 |
|
794 | |||
805 | fn inner_headrevs(&self, py: Python) -> PyResult<PyObject> { |
|
795 | fn inner_headrevs(&self, py: Python) -> PyResult<PyObject> { | |
806 | let index = &mut *self.index(py).borrow_mut(); |
|
796 | let index = &mut *self.index(py).borrow_mut(); | |
807 | let as_vec: Vec<PyObject> = index |
|
797 | let as_vec: Vec<PyObject> = index | |
808 | .head_revs() |
|
798 | .head_revs() | |
809 | .map_err(|e| graph_error(py, e))? |
|
799 | .map_err(|e| graph_error(py, e))? | |
810 | .iter() |
|
800 | .iter() | |
811 | .map(|r| PyRevision::from(*r).into_py_object(py).into_object()) |
|
801 | .map(|r| PyRevision::from(*r).into_py_object(py).into_object()) | |
812 | .collect(); |
|
802 | .collect(); | |
813 | Ok(PyList::new(py, &as_vec).into_object()) |
|
803 | Ok(PyList::new(py, &as_vec).into_object()) | |
814 | } |
|
804 | } | |
815 |
|
805 | |||
816 | fn inner_headrevsfiltered( |
|
806 | fn inner_headrevsfiltered( | |
817 | &self, |
|
807 | &self, | |
818 | py: Python, |
|
808 | py: Python, | |
819 | filtered_revs: &PyObject, |
|
809 | filtered_revs: &PyObject, | |
820 |
) -> PyResult< |
|
810 | ) -> PyResult<PyObject> { | |
821 | let index = &mut *self.index(py).borrow_mut(); |
|
811 | let index = &mut *self.index(py).borrow_mut(); | |
822 | let filtered_revs = rev_pyiter_collect(py, filtered_revs, index)?; |
|
812 | let filtered_revs = rev_pyiter_collect(py, filtered_revs, index)?; | |
823 |
|
813 | |||
824 | index |
|
814 | let as_vec: Vec<PyObject> = index | |
825 | .head_revs_filtered(&filtered_revs) |
|
815 | .head_revs_filtered(&filtered_revs) | |
826 |
.map_err(|e| |
|
816 | .map_err(|e| graph_error(py, e))? | |
|
817 | .iter() | |||
|
818 | .map(|r| PyRevision::from(*r).into_py_object(py).into_object()) | |||
|
819 | .collect(); | |||
|
820 | Ok(PyList::new(py, &as_vec).into_object()) | |||
827 | } |
|
821 | } | |
828 | } |
|
822 | } | |
829 |
|
823 | |||
830 | fn revlog_error(py: Python) -> PyErr { |
|
824 | fn revlog_error(py: Python) -> PyErr { | |
831 | match py |
|
825 | match py | |
832 | .import("mercurial.error") |
|
826 | .import("mercurial.error") | |
833 | .and_then(|m| m.get(py, "RevlogError")) |
|
827 | .and_then(|m| m.get(py, "RevlogError")) | |
834 | { |
|
828 | { | |
835 | Err(e) => e, |
|
829 | Err(e) => e, | |
836 | Ok(cls) => PyErr::from_instance( |
|
830 | Ok(cls) => PyErr::from_instance( | |
837 | py, |
|
831 | py, | |
838 | cls.call(py, (py.None(),), None).ok().into_py_object(py), |
|
832 | cls.call(py, (py.None(),), None).ok().into_py_object(py), | |
839 | ), |
|
833 | ), | |
840 | } |
|
834 | } | |
841 | } |
|
835 | } | |
842 |
|
836 | |||
843 | fn graph_error(py: Python, _err: hg::GraphError) -> PyErr { |
|
837 | fn graph_error(py: Python, _err: hg::GraphError) -> PyErr { | |
844 | // ParentOutOfRange is currently the only alternative |
|
838 | // ParentOutOfRange is currently the only alternative | |
845 | // in `hg::GraphError`. The C index always raises this simple ValueError. |
|
839 | // in `hg::GraphError`. The C index always raises this simple ValueError. | |
846 | PyErr::new::<ValueError, _>(py, "parent out of range") |
|
840 | PyErr::new::<ValueError, _>(py, "parent out of range") | |
847 | } |
|
841 | } | |
848 |
|
842 | |||
849 | fn nodemap_rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr { |
|
843 | fn nodemap_rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr { | |
850 | PyErr::new::<ValueError, _>( |
|
844 | PyErr::new::<ValueError, _>( | |
851 | py, |
|
845 | py, | |
852 | format!( |
|
846 | format!( | |
853 | "Inconsistency: Revision {} found in nodemap \ |
|
847 | "Inconsistency: Revision {} found in nodemap \ | |
854 | is not in revlog index", |
|
848 | is not in revlog index", | |
855 | rev |
|
849 | rev | |
856 | ), |
|
850 | ), | |
857 | ) |
|
851 | ) | |
858 | } |
|
852 | } | |
859 |
|
853 | |||
860 | fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr { |
|
854 | fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr { | |
861 | PyErr::new::<ValueError, _>( |
|
855 | PyErr::new::<ValueError, _>( | |
862 | py, |
|
856 | py, | |
863 | format!("revlog index out of range: {}", rev), |
|
857 | format!("revlog index out of range: {}", rev), | |
864 | ) |
|
858 | ) | |
865 | } |
|
859 | } | |
866 |
|
860 | |||
867 | /// Standard treatment of NodeMapError |
|
861 | /// Standard treatment of NodeMapError | |
868 | fn nodemap_error(py: Python, err: NodeMapError) -> PyErr { |
|
862 | fn nodemap_error(py: Python, err: NodeMapError) -> PyErr { | |
869 | match err { |
|
863 | match err { | |
870 | NodeMapError::MultipleResults => revlog_error(py), |
|
864 | NodeMapError::MultipleResults => revlog_error(py), | |
871 | NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r), |
|
865 | NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r), | |
872 | } |
|
866 | } | |
873 | } |
|
867 | } | |
874 |
|
868 | |||
875 | /// assert two Python objects to be equal from a Python point of view |
|
869 | /// assert two Python objects to be equal from a Python point of view | |
876 | /// |
|
870 | /// | |
877 | /// `method` is a label for the assertion error message, intended to be the |
|
871 | /// `method` is a label for the assertion error message, intended to be the | |
878 | /// name of the caller. |
|
872 | /// name of the caller. | |
879 | /// `normalizer` is a function that takes a Python variable name and returns |
|
873 | /// `normalizer` is a function that takes a Python variable name and returns | |
880 | /// an expression that the conparison will actually use. |
|
874 | /// an expression that the conparison will actually use. | |
881 | /// Foe example: `|v| format!("sorted({})", v)` |
|
875 | /// Foe example: `|v| format!("sorted({})", v)` | |
882 | fn assert_py_eq_normalized( |
|
876 | fn assert_py_eq_normalized( | |
883 | py: Python, |
|
877 | py: Python, | |
884 | method: &str, |
|
878 | method: &str, | |
885 | rust: &PyObject, |
|
879 | rust: &PyObject, | |
886 | c: &PyObject, |
|
880 | c: &PyObject, | |
887 | normalizer: impl FnOnce(&str) -> String + Copy, |
|
881 | normalizer: impl FnOnce(&str) -> String + Copy, | |
888 | ) -> PyResult<()> { |
|
882 | ) -> PyResult<()> { | |
889 | let locals = PyDict::new(py); |
|
883 | let locals = PyDict::new(py); | |
890 | locals.set_item(py, "rust".into_py_object(py).into_object(), rust)?; |
|
884 | locals.set_item(py, "rust".into_py_object(py).into_object(), rust)?; | |
891 | locals.set_item(py, "c".into_py_object(py).into_object(), c)?; |
|
885 | locals.set_item(py, "c".into_py_object(py).into_object(), c)?; | |
892 | // let lhs = format!(normalizer_fmt, "rust"); |
|
886 | // let lhs = format!(normalizer_fmt, "rust"); | |
893 | // let rhs = format!(normalizer_fmt, "c"); |
|
887 | // let rhs = format!(normalizer_fmt, "c"); | |
894 | let is_eq: PyBool = py |
|
888 | let is_eq: PyBool = py | |
895 | .eval( |
|
889 | .eval( | |
896 | &format!("{} == {}", &normalizer("rust"), &normalizer("c")), |
|
890 | &format!("{} == {}", &normalizer("rust"), &normalizer("c")), | |
897 | None, |
|
891 | None, | |
898 | Some(&locals), |
|
892 | Some(&locals), | |
899 | )? |
|
893 | )? | |
900 | .extract(py)?; |
|
894 | .extract(py)?; | |
901 | assert!( |
|
895 | assert!( | |
902 | is_eq.is_true(), |
|
896 | is_eq.is_true(), | |
903 | "{} results differ. Rust: {:?} C: {:?} (before any normalization)", |
|
897 | "{} results differ. Rust: {:?} C: {:?} (before any normalization)", | |
904 | method, |
|
898 | method, | |
905 | rust, |
|
899 | rust, | |
906 | c |
|
900 | c | |
907 | ); |
|
901 | ); | |
908 | Ok(()) |
|
902 | Ok(()) | |
909 | } |
|
903 | } | |
910 |
|
904 | |||
911 | fn assert_py_eq( |
|
905 | fn assert_py_eq( | |
912 | py: Python, |
|
906 | py: Python, | |
913 | method: &str, |
|
907 | method: &str, | |
914 | rust: &PyObject, |
|
908 | rust: &PyObject, | |
915 | c: &PyObject, |
|
909 | c: &PyObject, | |
916 | ) -> PyResult<()> { |
|
910 | ) -> PyResult<()> { | |
917 | assert_py_eq_normalized(py, method, rust, c, |v| v.to_owned()) |
|
911 | assert_py_eq_normalized(py, method, rust, c, |v| v.to_owned()) | |
918 | } |
|
912 | } | |
919 |
|
913 | |||
920 | /// Create the module, with __package__ given from parent |
|
914 | /// Create the module, with __package__ given from parent | |
921 | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { |
|
915 | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | |
922 | let dotted_name = &format!("{}.revlog", package); |
|
916 | let dotted_name = &format!("{}.revlog", package); | |
923 | let m = PyModule::new(py, dotted_name)?; |
|
917 | let m = PyModule::new(py, dotted_name)?; | |
924 | m.add(py, "__package__", package)?; |
|
918 | m.add(py, "__package__", package)?; | |
925 | m.add(py, "__doc__", "RevLog - Rust implementations")?; |
|
919 | m.add(py, "__doc__", "RevLog - Rust implementations")?; | |
926 |
|
920 | |||
927 | m.add_class::<MixedIndex>(py)?; |
|
921 | m.add_class::<MixedIndex>(py)?; | |
928 |
|
922 | |||
929 | let sys = PyModule::import(py, "sys")?; |
|
923 | let sys = PyModule::import(py, "sys")?; | |
930 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
|
924 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; | |
931 | sys_modules.set_item(py, dotted_name, &m)?; |
|
925 | sys_modules.set_item(py, dotted_name, &m)?; | |
932 |
|
926 | |||
933 | Ok(m) |
|
927 | Ok(m) | |
934 | } |
|
928 | } |
General Comments 0
You need to be logged in to leave comments.
Login now