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