Mercurial > public > mercurial-scm > hg-stable
diff rust/hg-cpython/src/revlog.rs @ 51260:7eea2e4109ae
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set
Since there is no Rust implementation for REVLOGV2/CHANGELOGv2, we declare
them to be incompatible with Rust, hence indexes in these formats will use
the implementations from Python `mercurial.ancestor`. If this is an unacceptable
performance hit for current users of these formats, we can later on add Rust
implementations based on the C index for them or implement these formats for
the Rust indexes.
Among the challenges that we had to meet, we wanted to avoid taking the GIL each
time the inner (vcsgraph) iterator has to call the parents function. This would probably
still be acceptable in terms of performance with `AncestorsIterator`, but not with
`LazyAncestors` nor for the upcoming change in `MissingAncestors`.
Hence we enclose the reference to the index in a `PySharedRef`, leading to more
rigourous checking of mutations, which does pass now that there no logically immutable
methods of `hg::index::Index` that take a mutable reference as input.
author | Georges Racinet <georges.racinet@octobus.net> |
---|---|
date | Fri, 27 Oct 2023 22:11:05 +0200 |
parents | 456e0fe702e8 |
children | 0b81440e2a73 |
line wrap: on
line diff
--- a/rust/hg-cpython/src/revlog.rs Fri Oct 27 23:29:29 2023 +0200 +++ b/rust/hg-cpython/src/revlog.rs Fri Oct 27 22:11:05 2023 +0200 @@ -16,7 +16,7 @@ exc::{IndexError, ValueError}, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList, PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python, - PythonObject, ToPyObject, + PythonObject, ToPyObject, UnsafePyLeaked, }; use hg::{ errors::HgError, @@ -25,10 +25,11 @@ INDEX_ENTRY_SIZE, }, nodemap::{Block, NodeMapError, NodeTree}, - revlog::{nodemap::NodeMap, NodePrefix, RevlogError, RevlogIndex}, - BaseRevision, Revision, UncheckedRevision, NULL_REVISION, + revlog::{nodemap::NodeMap, Graph, NodePrefix, RevlogError, RevlogIndex}, + BaseRevision, Node, Revision, UncheckedRevision, NULL_REVISION, }; use std::{cell::RefCell, collections::HashMap}; +use vcsgraph::graph::Graph as VCSGraph; /// Return a Struct implementing the Graph trait pub(crate) fn pyindex_to_graph( @@ -41,9 +42,64 @@ } } +pub struct PySharedIndex { + /// The underlying hg-core index + pub(crate) inner: &'static hg::index::Index, +} + +/// Return a Struct implementing the Graph trait +pub(crate) fn py_rust_index_to_graph( + py: Python, + index: PyObject, +) -> PyResult<UnsafePyLeaked<PySharedIndex>> { + let midx = index.extract::<MixedIndex>(py)?; + let leaked = midx.index(py).leak_immutable(); + Ok(unsafe { leaked.map(py, |idx| PySharedIndex { inner: idx }) }) +} + +impl Clone for PySharedIndex { + fn clone(&self) -> Self { + Self { inner: self.inner } + } +} + +impl Graph for PySharedIndex { + fn parents(&self, rev: Revision) -> Result<[Revision; 2], hg::GraphError> { + self.inner.parents(rev) + } +} + +impl VCSGraph for PySharedIndex { + fn parents( + &self, + rev: BaseRevision, + ) -> Result<vcsgraph::graph::Parents, vcsgraph::graph::GraphReadError> + { + // FIXME This trait should be reworked to decide between Revision + // and UncheckedRevision, get better errors names, etc. + match Graph::parents(self, Revision(rev)) { + Ok(parents) => { + Ok(vcsgraph::graph::Parents([parents[0].0, parents[1].0])) + } + Err(hg::GraphError::ParentOutOfRange(rev)) => { + Err(vcsgraph::graph::GraphReadError::KeyedInvalidKey(rev.0)) + } + } + } +} + +impl RevlogIndex for PySharedIndex { + fn len(&self) -> usize { + self.inner.len() + } + fn node(&self, rev: Revision) -> Option<&Node> { + self.inner.node(rev) + } +} + py_class!(pub class MixedIndex |py| { data cindex: RefCell<cindex::Index>; - data index: RefCell<hg::index::Index>; + @shared data index: hg::index::Index; data nt: RefCell<Option<NodeTree>>; data docket: RefCell<Option<PyObject>>; // Holds a reference to the mmap'ed persistent nodemap data @@ -668,17 +724,15 @@ Self::create_instance( py, RefCell::new(cindex::Index::new(py, cindex)?), - RefCell::new( - hg::index::Index::new( - bytes, - IndexHeader::parse(&header.to_be_bytes()) - .expect("default header is broken") - .unwrap(), - ) - .map_err(|e| { - revlog_error_with_msg(py, e.to_string().as_bytes()) - })?, - ), + hg::index::Index::new( + bytes, + IndexHeader::parse(&header.to_be_bytes()) + .expect("default header is broken") + .unwrap(), + ) + .map_err(|e| { + revlog_error_with_msg(py, e.to_string().as_bytes()) + })?, RefCell::new(None), RefCell::new(None), RefCell::new(None),