comparison rust/hg-cpython/src/revlog.rs @ 51239: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
comparison
equal deleted inserted replaced
51238:633408a0f2e2 51239:7eea2e4109ae
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, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList,
18 PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python, 18 PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python,
19 PythonObject, ToPyObject, 19 PythonObject, 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, NodePrefix, RevlogError, RevlogIndex}, 28 revlog::{nodemap::NodeMap, Graph, NodePrefix, RevlogError, RevlogIndex},
29 BaseRevision, 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 33
33 /// Return a Struct implementing the Graph trait 34 /// Return a Struct implementing the Graph trait
34 pub(crate) fn pyindex_to_graph( 35 pub(crate) fn pyindex_to_graph(
35 py: Python, 36 py: Python,
36 index: PyObject, 37 index: PyObject,
39 Ok(midx) => Ok(midx.clone_cindex(py)), 40 Ok(midx) => Ok(midx.clone_cindex(py)),
40 Err(_) => cindex::Index::new(py, index), 41 Err(_) => cindex::Index::new(py, index),
41 } 42 }
42 } 43 }
43 44
45 pub struct PySharedIndex {
46 /// The underlying hg-core index
47 pub(crate) inner: &'static hg::index::Index,
48 }
49
50 /// Return a Struct implementing the Graph trait
51 pub(crate) fn py_rust_index_to_graph(
52 py: Python,
53 index: PyObject,
54 ) -> PyResult<UnsafePyLeaked<PySharedIndex>> {
55 let midx = index.extract::<MixedIndex>(py)?;
56 let leaked = midx.index(py).leak_immutable();
57 Ok(unsafe { leaked.map(py, |idx| PySharedIndex { inner: idx }) })
58 }
59
60 impl Clone for PySharedIndex {
61 fn clone(&self) -> Self {
62 Self { inner: self.inner }
63 }
64 }
65
66 impl Graph for PySharedIndex {
67 fn parents(&self, rev: Revision) -> Result<[Revision; 2], hg::GraphError> {
68 self.inner.parents(rev)
69 }
70 }
71
72 impl VCSGraph for PySharedIndex {
73 fn parents(
74 &self,
75 rev: BaseRevision,
76 ) -> Result<vcsgraph::graph::Parents, vcsgraph::graph::GraphReadError>
77 {
78 // FIXME This trait should be reworked to decide between Revision
79 // and UncheckedRevision, get better errors names, etc.
80 match Graph::parents(self, Revision(rev)) {
81 Ok(parents) => {
82 Ok(vcsgraph::graph::Parents([parents[0].0, parents[1].0]))
83 }
84 Err(hg::GraphError::ParentOutOfRange(rev)) => {
85 Err(vcsgraph::graph::GraphReadError::KeyedInvalidKey(rev.0))
86 }
87 }
88 }
89 }
90
91 impl RevlogIndex for PySharedIndex {
92 fn len(&self) -> usize {
93 self.inner.len()
94 }
95 fn node(&self, rev: Revision) -> Option<&Node> {
96 self.inner.node(rev)
97 }
98 }
99
44 py_class!(pub class MixedIndex |py| { 100 py_class!(pub class MixedIndex |py| {
45 data cindex: RefCell<cindex::Index>; 101 data cindex: RefCell<cindex::Index>;
46 data index: RefCell<hg::index::Index>; 102 @shared data index: hg::index::Index;
47 data nt: RefCell<Option<NodeTree>>; 103 data nt: RefCell<Option<NodeTree>>;
48 data docket: RefCell<Option<PyObject>>; 104 data docket: RefCell<Option<PyObject>>;
49 // Holds a reference to the mmap'ed persistent nodemap data 105 // Holds a reference to the mmap'ed persistent nodemap data
50 data nodemap_mmap: RefCell<Option<PyBuffer>>; 106 data nodemap_mmap: RefCell<Option<PyBuffer>>;
51 // Holds a reference to the mmap'ed persistent index data 107 // Holds a reference to the mmap'ed persistent index data
666 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? }; 722 let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
667 723
668 Self::create_instance( 724 Self::create_instance(
669 py, 725 py,
670 RefCell::new(cindex::Index::new(py, cindex)?), 726 RefCell::new(cindex::Index::new(py, cindex)?),
671 RefCell::new( 727 hg::index::Index::new(
672 hg::index::Index::new( 728 bytes,
673 bytes, 729 IndexHeader::parse(&header.to_be_bytes())
674 IndexHeader::parse(&header.to_be_bytes()) 730 .expect("default header is broken")
675 .expect("default header is broken") 731 .unwrap(),
676 .unwrap(), 732 )
677 ) 733 .map_err(|e| {
678 .map_err(|e| { 734 revlog_error_with_msg(py, e.to_string().as_bytes())
679 revlog_error_with_msg(py, e.to_string().as_bytes()) 735 })?,
680 })?,
681 ),
682 RefCell::new(None), 736 RefCell::new(None),
683 RefCell::new(None), 737 RefCell::new(None),
684 RefCell::new(None), 738 RefCell::new(None),
685 RefCell::new(Some(buf)), 739 RefCell::new(Some(buf)),
686 ) 740 )