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