Mercurial > hg
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 ) |