##// END OF EJS Templates
rust-index: use a `BitVec` instead of plain `Vec` for heads computation...
rust-index: use a `BitVec` instead of plain `Vec` for heads computation The `Vec` method uses one byte per revision, this uses 1 per 8 revisions, which improves our memory footprint. For large graphs (10+ millions), this can make a measurable difference server-side. I have seen no measurable impact on execution speed.

File last commit:

r52151:8b89f7cc default
r52153:c4f1a790 default
Show More
revlog.rs
1126 lines | 38.5 KiB | application/rls-services+xml | RustLexer
rust-index: add a function to convert PyObject index for hg-core...
r44398 // revlog.rs
//
Georges Racinet
rust-nodemap: add utils for propagating errors...
r44993 // Copyright 2019-2020 Georges Racinet <georges.racinet@octobus.net>
rust-index: add a function to convert PyObject index for hg-core...
r44398 //
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 use crate::{
Raphaël Gomès
rust-index: add support for `reachableroots2`...
r52115 conversion::{rev_pyiter_collect, rev_pyiter_collect_or_else},
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 utils::{node_from_py_bytes, node_from_py_object},
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 PyRevision,
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 };
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 use cpython::{
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997 buffer::{Element, PyBuffer},
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 exc::{IndexError, ValueError},
Georges Racinet
rust-index: a property to identify the Rust index as such...
r52141 ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyInt, PyList,
PyModule, PyObject, PyResult, PySet, PyString, PyTuple, Python,
PythonObject, ToPyObject, UnsafePyLeaked,
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 };
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 use hg::{
Raphaël Gomès
rust-index: add support for `find_snapshots`
r52105 errors::HgError,
Georges Racinet
rust-index: optimize find_gca_candidates() on less than 8 revisions...
r52122 index::{
IndexHeader, Phase, RevisionDataParams, SnapshotsCache,
INDEX_ENTRY_SIZE,
},
Georges Racinet
rust-cpython-revlog: renamed NodeTree import as CoreNodeTree...
r52140 nodemap::{Block, NodeMapError, NodeTree as CoreNodeTree},
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 revlog::{nodemap::NodeMap, Graph, NodePrefix, RevlogError, RevlogIndex},
BaseRevision, Node, Revision, UncheckedRevision, NULL_REVISION,
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 };
Raphaël Gomès
rust-index: add support for `computephasesmapsets`...
r52113 use std::{cell::RefCell, collections::HashMap};
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 use vcsgraph::graph::Graph as VCSGraph;
rust-index: add a function to convert PyObject index for hg-core...
r44398
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 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>> {
Georges Racinet on incendie.racinet.fr
rust-index: renamed `MixedIndex` as `Index`...
r52147 let midx = index.extract::<Index>(py)?;
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 let leaked = midx.index(py).leak_immutable();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of the `UnsafePyLeaked`
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 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 {
Raphaël Gomès
rust-index: allow inlining VCSGraph parents across crates
r52151 #[inline(always)]
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 fn parents(&self, rev: Revision) -> Result<[Revision; 2], hg::GraphError> {
self.inner.parents(rev)
}
}
impl VCSGraph for PySharedIndex {
Raphaël Gomès
rust-index: allow inlining VCSGraph parents across crates
r52151 #[inline(always)]
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 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)
}
}
Georges Racinet on incendie.racinet.fr
rust-index: renamed `MixedIndex` as `Index`...
r52147 py_class!(pub class Index |py| {
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 @shared data index: hg::index::Index;
Georges Racinet
rust-cpython-revlog: renamed NodeTree import as CoreNodeTree...
r52140 data nt: RefCell<Option<CoreNodeTree>>;
Georges Racinet
rust-nodemap: add binding for `nodemap_data_incremental`...
r44996 data docket: RefCell<Option<PyObject>>;
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997 // Holds a reference to the mmap'ed persistent nodemap data
Raphaël Gomès
rust-mixed-index: rename variable to make the next change clearer...
r52079 data nodemap_mmap: RefCell<Option<PyBuffer>>;
Raphaël Gomès
rust-index: pass data down to the Rust index...
r52083 // Holds a reference to the mmap'ed persistent index data
data index_mmap: RefCell<Option<PyBuffer>>;
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413
Raphaël Gomès
rust-index: pass data down to the Rust index...
r52083 def __new__(
_cls,
Raphaël Gomès
rust-revlog: teach the revlog opening code to read the repo options...
r52084 data: PyObject,
default_header: u32,
Georges Racinet on incendie.racinet.fr
rust-index: renamed `MixedIndex` as `Index`...
r52147 ) -> PyResult<Self> {
Georges Racinet
rust-index: stop instantiating a C Index...
r52146 Self::new(py, data, default_header)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
Georges Racinet
rust-index: expose a method to retrieve the C index...
r44464 /// Compatibility layer used for Python consumers needing access to the C index
///
/// Only use case so far is `scmutil.shortesthexnodeidprefix`,
/// that may need to build a custom `nodetree`, based on a specified revset.
/// With a Rust implementation of the nodemap, we will be able to get rid of
/// this, by exposing our own standalone nodemap class,
Georges Racinet
rust-index: stop instantiating a C Index...
r52146 /// ready to accept `Index`.
/* def get_cindex(&self) -> PyResult<PyObject> {
Georges Racinet
rust-index: expose a method to retrieve the C index...
r44464 Ok(self.cindex(py).borrow().inner().clone_ref(py))
}
Georges Racinet
rust-index: stop instantiating a C Index...
r52146 */
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 // Index API involving nodemap, as defined in mercurial/pure/parsers.py
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 /// Return Revision if found, raises a bare `error.RevlogError`
/// in case of ambiguity, same as C version does
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def get_rev(&self, node: PyBytes) -> PyResult<Option<PyRevision>> {
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 let opt = self.get_nodetree(py)?.borrow();
let nt = opt.as_ref().unwrap();
Raphaël Gomès
rust-index: check rindex and cindex return the same get_rev...
r52089 let ridx = &*self.index(py).borrow();
Georges Racinet
rust-nodemap: backed out mitigation for issue 6554...
r49090 let node = node_from_py_bytes(py, &node)?;
Raphaël Gomès
rust-index: check rindex and cindex return the same get_rev...
r52089 let rust_rev =
nt.find_bin(ridx, node.into()).map_err(|e| nodemap_error(py, e))?;
Ok(rust_rev.map(Into::into))
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 }
/// same as `get_rev()` but raises a bare `error.RevlogError` if node
/// is not found.
///
/// No need to repeat `node` in the exception, `mercurial/revlog.py`
/// will catch and rewrap with it
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 def rev(&self, node: PyBytes) -> PyResult<PyRevision> {
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 self.get_rev(py, node)?.ok_or_else(|| revlog_error(py))
}
/// return True if the node exist in the index
def has_node(&self, node: PyBytes) -> PyResult<bool> {
Georges Racinet
rust-index: optim note for post-scaffolding removal
r52097 // TODO OPTIM we could avoid a needless conversion here,
// to do when scaffolding for pure Rust switch is removed,
// as `get_rev()` currently does the necessary assertions
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 self.get_rev(py, node).map(|opt| opt.is_some())
}
/// find length of shortest hex nodeid of a binary ID
def shortest(&self, node: PyBytes) -> PyResult<usize> {
let opt = self.get_nodetree(py)?.borrow();
let nt = opt.as_ref().unwrap();
Raphaël Gomès
rust-index: use the rust index in `shortest`
r52101 let idx = &*self.index(py).borrow();
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
{
Ok(Some(l)) => Ok(l),
Ok(None) => Err(revlog_error(py)),
Err(e) => Err(nodemap_error(py, e)),
}
}
Georges Racinet
rust-nodemap: backed out mitigation for issue 6554...
r49090 def partialmatch(&self, node: PyObject) -> PyResult<Option<PyBytes>> {
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 let opt = self.get_nodetree(py)?.borrow();
let nt = opt.as_ref().unwrap();
Raphaël Gomès
rust-index: use the Rust index in `partialmatch`
r52103 let idx = &*self.index(py).borrow();
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994
let node_as_string = if cfg!(feature = "python3-sys") {
Georges Racinet
rust-nodemap: backed out mitigation for issue 6554...
r49090 node.cast_as::<PyString>(py)?.to_string(py)?.to_string()
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 }
else {
Georges Racinet
rust-nodemap: backed out mitigation for issue 6554...
r49090 let node = node.extract::<PyBytes>(py)?;
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 String::from_utf8_lossy(node.data(py)).to_string()
};
Raphaël Gomès
rust: don't swallow valuable error information...
r50269 let prefix = NodePrefix::from_hex(&node_as_string)
.map_err(|_| PyErr::new::<ValueError, _>(
py, format!("Invalid node or prefix '{}'", node_as_string))
)?;
Simon Sapin
rust: Remove hex parsing from the nodemap...
r47161
Georges Racinet
rust-nodemap: backed out mitigation for issue 6554...
r49090 nt.find_bin(idx, prefix)
// TODO make an inner API returning the node directly
.map(|opt| opt.map(
|rev| PyBytes::new(py, idx.node(rev).unwrap().as_bytes())))
.map_err(|e| nodemap_error(py, e))
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 }
/// append an index entry
def append(&self, tup: PyTuple) -> PyResult<PyObject> {
if tup.len(py) < 8 {
// this is better than the panic promised by tup.get_item()
return Err(
PyErr::new::<IndexError, _>(py, "tuple index out of range"))
}
let node_bytes = tup.get_item(py, 7).extract(py)?;
let node = node_from_py_object(py, &node_bytes)?;
Raphaël Gomès
rust-index: check equality between rust and cindex for `__len__`
r52086 let rev = self.len(py)? as BaseRevision;
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 // This is ok since we will just add the revision to the index
Raphaël Gomès
rust-index: check equality between rust and cindex for `__len__`
r52086 let rev = Revision(rev);
Raphaël Gomès
rust-index: synchronize append method...
r52085 self.index(py)
.borrow_mut()
.append(py_tuple_to_revision_data_params(py, tup)?)
.unwrap();
Georges Racinet
rust-index: stop using C index...
r52139 let idx = &*self.index(py).borrow();
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 self.get_nodetree(py)?.borrow_mut().as_mut().unwrap()
Georges Racinet
rust-index: stop using C index...
r52139 .insert(idx, &node, rev)
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 .map_err(|e| nodemap_error(py, e))?;
Ok(py.None())
}
def __delitem__(&self, key: PyObject) -> PyResult<()> {
// __delitem__ is both for `del idx[r]` and `del idx[r1:r2]`
Raphaël Gomès
rust-index: add support for `del index[r]`...
r52143 let start = if let Ok(rev) = key.extract(py) {
UncheckedRevision(rev)
} else {
let start = key.getattr(py, "start")?;
UncheckedRevision(start.extract(py)?)
};
Raphaël Gomès
rust-index: synchronize remove to Rust index...
r52088 let start = self.index(py)
.borrow()
.check_revision(start)
.ok_or_else(|| {
nodemap_error(py, NodeMapError::RevisionNotInIndex(start))
})?;
self.index(py).borrow_mut().remove(start).unwrap();
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 let mut opt = self.get_nodetree(py)?.borrow_mut();
Raphaël Gomès
rust: run `cargo clippy`...
r50809 let nt = opt.as_mut().unwrap();
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 nt.invalidate_all();
Raphaël Gomès
rust: run `cargo clippy`...
r50809 self.fill_nodemap(py, nt)?;
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 Ok(())
}
//
Georges Racinet
rust-index: stop using C index...
r52139 // Index methods previously reforwarded to C index (tp_methods)
// Same ordering as in revlog.c
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 //
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413
/// return the gca set of the given revs
Georges Racinet
rust-index: stop using C index...
r52139 def ancestors(&self, *args, **_kw) -> PyResult<PyObject> {
Georges Racinet
rust-index: implement common_ancestors_heads() and ancestors()...
r52118 let rust_res = self.inner_ancestors(py, args)?;
Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
/// return the heads of the common ancestors of the given revs
Georges Racinet
rust-index: stop using C index...
r52139 def commonancestorsheads(&self, *args, **_kw) -> PyResult<PyObject> {
Georges Racinet
rust-index: implement common_ancestors_heads() and ancestors()...
r52118 let rust_res = self.inner_commonancestorsheads(py, args)?;
Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
Georges Racinet
rust-nodemap: also clear Rust data in `clearcaches`...
r44998 /// Clear the index caches and inner py_class data.
/// It is Python's responsibility to call `update_nodemap_data` again.
Georges Racinet
rust-index: stop using C index...
r52139 def clearcaches(&self) -> PyResult<PyObject> {
Georges Racinet
rust-nodemap: also clear Rust data in `clearcaches`...
r44998 self.nt(py).borrow_mut().take();
self.docket(py).borrow_mut().take();
Raphaël Gomès
rust-mixed-index: rename variable to make the next change clearer...
r52079 self.nodemap_mmap(py).borrow_mut().take();
Georges Racinet on incendie.racinet.fr
rust-index: use interior mutability in head revs and caches...
r52127 self.index(py).borrow().clear_caches();
Georges Racinet
rust-index: stop using C index...
r52139 Ok(py.None())
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
revlog: add a `entry_binary` method on index...
r47808 /// return the raw binary string representing a revision
Georges Racinet
rust-index: stop using C index...
r52139 def entry_binary(&self, *args, **_kw) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: check that the entry bytes are the same in both indexes...
r52096 let rindex = self.index(py).borrow();
let rev = UncheckedRevision(args.get_item(py, 0).extract(py)?);
let rust_bytes = rindex.check_revision(rev).and_then(
|r| rindex.entry_binary(r))
.ok_or_else(|| rev_not_in_index(py, rev))?;
let rust_res = PyBytes::new(py, rust_bytes).into_object();
Ok(rust_res)
revlog: add a `entry_binary` method on index...
r47808 }
revlog: have an explicit "pack_header" method...
r47811 /// return a binary packed version of the header
Georges Racinet
rust-index: stop using C index...
r52139 def pack_header(&self, *args, **_kw) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: add `pack_header` support
r52091 let rindex = self.index(py).borrow();
let packed = rindex.pack_header(args.get_item(py, 0).extract(py)?);
Georges Racinet
rust-index: return variables systematic naming convention...
r52095 let rust_res = PyBytes::new(py, &packed).into_object();
Ok(rust_res)
revlog: have an explicit "pack_header" method...
r47811 }
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 /// compute phases
Georges Racinet
rust-index: stop using C index...
r52139 def computephasesmapsets(&self, *args, **_kw) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: add support for `computephasesmapsets`...
r52113 let py_roots = args.get_item(py, 0).extract::<PyDict>(py)?;
let rust_res = self.inner_computephasesmapsets(py, py_roots)?;
Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
/// reachableroots
Georges Racinet
rust-index: stop using C index...
r52139 def reachableroots2(&self, *args, **_kw) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: add support for `reachableroots2`...
r52115 let rust_res = self.inner_reachableroots2(
py,
UncheckedRevision(args.get_item(py, 0).extract(py)?),
args.get_item(py, 1),
args.get_item(py, 2),
args.get_item(py, 3).extract(py)?,
)?;
Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
/// get head revisions
Georges Racinet
rust-index: stop using C index...
r52139 def headrevs(&self) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: implement headrevs
r52108 let rust_res = self.inner_headrevs(py)?;
Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
/// get filtered head revisions
Georges Racinet
rust-index: stop using C index...
r52139 def headrevsfiltered(&self, *args, **_kw) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: add support for `headrevsfiltered`...
r52109 let rust_res = self.inner_headrevsfiltered(py, &args.get_item(py, 0))?;
Georges Racinet
rust-index: headrevsfiltered() returning Rust result
r52110 Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
/// True if the object is a snapshot
Georges Racinet
rust-index: stop using C index...
r52139 def issnapshot(&self, *args, **_kw) -> PyResult<bool> {
Raphaël Gomès
rust-index: add `is_snapshot` method
r52104 let index = self.index(py).borrow();
let result = index
.is_snapshot(UncheckedRevision(args.get_item(py, 0).extract(py)?))
.map_err(|e| {
PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
})?;
Ok(result)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
/// Gather snapshot data in a cache dict
Georges Racinet
rust-index: stop using C index...
r52139 def findsnapshots(&self, *args, **_kw) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: add support for `find_snapshots`
r52105 let index = self.index(py).borrow();
let cache: PyDict = args.get_item(py, 0).extract(py)?;
// this methods operates by setting new values in the cache,
// hence we will compare results by letting the C implementation
// operate over a deepcopy of the cache, and finally compare both
// caches.
let c_cache = PyDict::new(py);
for (k, v) in cache.items(py) {
c_cache.set_item(py, k, PySet::new(py, v)?)?;
}
let start_rev = UncheckedRevision(args.get_item(py, 1).extract(py)?);
let end_rev = UncheckedRevision(args.get_item(py, 2).extract(py)?);
let mut cache_wrapper = PySnapshotsCache{ py, dict: cache };
index.find_snapshots(
start_rev,
end_rev,
&mut cache_wrapper,
).map_err(|_| revlog_error(py))?;
Ok(py.None())
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
/// determine revisions with deltas to reconstruct fulltext
Georges Racinet
rust-index: stop using C index...
r52139 def deltachain(&self, *args, **_kw) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: add support for delta-chain computation
r52106 let index = self.index(py).borrow();
let rev = args.get_item(py, 0).extract::<BaseRevision>(py)?.into();
let stop_rev =
args.get_item(py, 1).extract::<Option<BaseRevision>>(py)?;
let rev = index.check_revision(rev).ok_or_else(|| {
nodemap_error(py, NodeMapError::RevisionNotInIndex(rev))
})?;
let stop_rev = if let Some(stop_rev) = stop_rev {
let stop_rev = UncheckedRevision(stop_rev);
Some(index.check_revision(stop_rev).ok_or_else(|| {
nodemap_error(py, NodeMapError::RevisionNotInIndex(stop_rev))
})?)
} else {None};
Georges Racinet on incendie.racinet.fr
rust-index: honour incoming using_general_delta in `deltachain`...
r52128 let using_general_delta = args.get_item(py, 2)
.extract::<Option<u32>>(py)?
.map(|i| i != 0);
let (chain, stopped) = index.delta_chain(
rev, stop_rev, using_general_delta
).map_err(|e| {
Raphaël Gomès
rust-index: add support for delta-chain computation
r52106 PyErr::new::<cpython::exc::ValueError, _>(py, e.to_string())
})?;
let chain: Vec<_> = chain.into_iter().map(|r| r.0).collect();
Ok(
PyTuple::new(
py,
&[
chain.into_py_object(py).into_object(),
stopped.into_py_object(py).into_object()
]
).into_object()
)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
/// slice planned chunk read to reach a density threshold
Georges Racinet
rust-index: stop using C index...
r52139 def slicechunktodensity(&self, *args, **_kw) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: add support for `_slicechunktodensity`
r52111 let rust_res = self.inner_slicechunktodensity(
py,
args.get_item(py, 0),
args.get_item(py, 1).extract(py)?,
args.get_item(py, 2).extract(py)?
)?;
Georges Racinet
rust-index: slicechunktodensity returns Rust result...
r52112 Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
// index_sequence_methods and index_mapping_methods.
//
// Since we call back through the high level Python API,
// there's no point making a distinction between index_get
// and index_getitem.
Raphaël Gomès
rust-index: implementation of __getitem__...
r52098 // gracinet 2023: this above is no longer true for the pure Rust impl
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413
def __len__(&self) -> PyResult<usize> {
Raphaël Gomès
rust-index: check equality between rust and cindex for `__len__`
r52086 self.len(py)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: implementation of __getitem__...
r52098 let rust_res = self.inner_getitem(py, key.clone_ref(py))?;
Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
def __contains__(&self, item: PyObject) -> PyResult<bool> {
// ObjectProtocol does not seem to provide contains(), so
// this is an equivalent implementation of the index_contains()
// defined in revlog.c
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 match item.extract::<i32>(py) {
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 Ok(rev) => {
Raphaël Gomès
rust-index: check equality between rust and cindex for `__len__`
r52086 Ok(rev >= -1 && rev < self.len(py)? as BaseRevision)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
Err(_) => {
Raphaël Gomès
rust-index: add checks that `__contains__` is synchronized
r52100 let item_bytes: PyBytes = item.extract(py)?;
let rust_res = self.has_node(py, item_bytes)?;
Ok(rust_res)
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
}
}
Georges Racinet
rust-nodemap: add binding for `nodemap_data_all`...
r44995 def nodemap_data_all(&self) -> PyResult<PyBytes> {
self.inner_nodemap_data_all(py)
}
Georges Racinet
rust-nodemap: add binding for `nodemap_data_incremental`...
r44996 def nodemap_data_incremental(&self) -> PyResult<PyObject> {
self.inner_nodemap_data_incremental(py)
}
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997 def update_nodemap_data(
&self,
docket: PyObject,
nm_data: PyObject
) -> PyResult<PyObject> {
self.inner_update_nodemap_data(py, docket, nm_data)
}
revlog: replace revlog._io.size with a new revlog.index.entry_size...
r47736 @property
def entry_size(&self) -> PyResult<PyInt> {
Georges Racinet
rust-index: optimize find_gca_candidates() on less than 8 revisions...
r52122 let rust_res: PyInt = INDEX_ENTRY_SIZE.to_py_object(py);
Ok(rust_res)
revlog: replace revlog._io.size with a new revlog.index.entry_size...
r47736 }
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413
revlog: signal which revlog index are compatible with Rust...
r48042 @property
def rust_ext_compat(&self) -> PyResult<PyInt> {
Georges Racinet
rust-index: optimize find_gca_candidates() on less than 8 revisions...
r52122 // will be entirely removed when the Rust index yet useful to
// implement in Rust to detangle things when removing `self.cindex`
let rust_res: PyInt = 1.to_py_object(py);
Ok(rust_res)
revlog: signal which revlog index are compatible with Rust...
r48042 }
Georges Racinet
rust-index: a property to identify the Rust index as such...
r52141 @property
def is_rust(&self) -> PyResult<PyBool> {
Ok(false.to_py_object(py))
}
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 });
Raphaël Gomès
rust-mixed-index: move the mmap keepalive into a function...
r52080 /// Take a (potentially) mmap'ed buffer, and return the underlying Python
/// buffer along with the Rust slice into said buffer. We need to keep the
/// Python buffer around, otherwise we'd get a dangling pointer once the buffer
/// is freed from Python's side.
///
/// # Safety
///
/// The caller must make sure that the buffer is kept around for at least as
/// long as the slice.
#[deny(unsafe_op_in_unsafe_fn)]
unsafe fn mmap_keeparound(
py: Python,
data: PyObject,
) -> PyResult<(
PyBuffer,
Raphaël Gomès
rust-index: add Sync bound to all relevant mmap-derived values...
r52126 Box<dyn std::ops::Deref<Target = [u8]> + Send + Sync + 'static>,
Raphaël Gomès
rust-mixed-index: move the mmap keepalive into a function...
r52080 )> {
let buf = PyBuffer::get(py, &data)?;
let len = buf.item_count();
// Build a slice from the mmap'ed buffer data
let cbuf = buf.buf_ptr();
let bytes = if std::mem::size_of::<u8>() == buf.item_size()
&& buf.is_c_contiguous()
&& u8::is_compatible_format(buf.format())
{
unsafe { std::slice::from_raw_parts(cbuf as *const u8, len) }
} else {
return Err(PyErr::new::<ValueError, _>(
py,
"Nodemap data buffer has an invalid memory representation"
.to_string(),
));
};
Ok((buf, Box::new(bytes)))
}
Raphaël Gomès
rust-index: synchronize append method...
r52085 fn py_tuple_to_revision_data_params(
py: Python,
tuple: PyTuple,
) -> PyResult<RevisionDataParams> {
if tuple.len(py) < 8 {
// this is better than the panic promised by tup.get_item()
return Err(PyErr::new::<IndexError, _>(
py,
"tuple index out of range",
));
}
let offset_or_flags: u64 = tuple.get_item(py, 0).extract(py)?;
let node_id = tuple
.get_item(py, 7)
.extract::<PyBytes>(py)?
.data(py)
.try_into()
.unwrap();
let flags = (offset_or_flags & 0xFFFF) as u16;
let data_offset = offset_or_flags >> 16;
Ok(RevisionDataParams {
flags,
data_offset,
data_compressed_length: tuple.get_item(py, 1).extract(py)?,
data_uncompressed_length: tuple.get_item(py, 2).extract(py)?,
data_delta_base: tuple.get_item(py, 3).extract(py)?,
link_rev: tuple.get_item(py, 4).extract(py)?,
parent_rev_1: tuple.get_item(py, 5).extract(py)?,
parent_rev_2: tuple.get_item(py, 6).extract(py)?,
node_id,
Raphaël Gomès
rust-index: implementation of __getitem__...
r52098 ..Default::default()
Raphaël Gomès
rust-index: synchronize append method...
r52085 })
}
Raphaël Gomès
rust-index: implementation of __getitem__...
r52098 fn revision_data_params_to_py_tuple(
py: Python,
params: RevisionDataParams,
) -> PyTuple {
PyTuple::new(
py,
&[
params.data_offset.into_py_object(py).into_object(),
params
.data_compressed_length
.into_py_object(py)
.into_object(),
params
.data_uncompressed_length
.into_py_object(py)
.into_object(),
params.data_delta_base.into_py_object(py).into_object(),
params.link_rev.into_py_object(py).into_object(),
params.parent_rev_1.into_py_object(py).into_object(),
params.parent_rev_2.into_py_object(py).into_object(),
PyBytes::new(py, &params.node_id)
.into_py_object(py)
.into_object(),
params._sidedata_offset.into_py_object(py).into_object(),
params
._sidedata_compressed_length
.into_py_object(py)
.into_object(),
params
.data_compression_mode
.into_py_object(py)
.into_object(),
params
._sidedata_compression_mode
.into_py_object(py)
.into_object(),
params._rank.into_py_object(py).into_object(),
],
)
}
Raphaël Gomès
rust-index: synchronize append method...
r52085
Raphaël Gomès
rust-index: add support for `find_snapshots`
r52105 struct PySnapshotsCache<'p> {
py: Python<'p>,
dict: PyDict,
}
impl<'p> SnapshotsCache for PySnapshotsCache<'p> {
fn insert_for(
&mut self,
rev: BaseRevision,
value: BaseRevision,
) -> Result<(), RevlogError> {
let pyvalue = value.into_py_object(self.py).into_object();
match self.dict.get_item(self.py, rev) {
Some(obj) => obj
.extract::<PySet>(self.py)
.and_then(|set| set.add(self.py, pyvalue)),
None => PySet::new(self.py, vec![pyvalue])
.and_then(|set| self.dict.set_item(self.py, rev, set)),
}
.map_err(|_| {
RevlogError::Other(HgError::unsupported(
"Error in Python caches handling",
))
})
}
}
Georges Racinet on incendie.racinet.fr
rust-index: renamed `MixedIndex` as `Index`...
r52147 impl Index {
fn new(py: Python, data: PyObject, header: u32) -> PyResult<Self> {
Raphaël Gomès
rust-index: pass data down to the Rust index...
r52083 // Safety: we keep the buffer around inside the class as `index_mmap`
let (buf, bytes) = unsafe { mmap_keeparound(py, data)? };
Georges Racinet
rust-index: moved constructor in separate impl block...
r44990 Self::create_instance(
py,
Georges Racinet
rust-index: using the `hg::index::Index` in ancestors iterator and lazy set...
r52132 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())
})?,
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 RefCell::new(None),
Georges Racinet
rust-nodemap: add binding for `nodemap_data_incremental`...
r44996 RefCell::new(None),
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997 RefCell::new(None),
Raphaël Gomès
rust-index: pass data down to the Rust index...
r52083 RefCell::new(Some(buf)),
Georges Racinet
rust-index: moved constructor in separate impl block...
r44990 )
}
Raphaël Gomès
rust-index: check equality between rust and cindex for `__len__`
r52086 fn len(&self, py: Python) -> PyResult<usize> {
let rust_index_len = self.index(py).borrow().len();
Georges Racinet
rust-index: optimize find_gca_candidates() on less than 8 revisions...
r52122 Ok(rust_index_len)
Raphaël Gomès
rust-index: check equality between rust and cindex for `__len__`
r52086 }
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 /// This is scaffolding at this point, but it could also become
/// a way to start a persistent nodemap or perform a
/// vacuum / repack operation
fn fill_nodemap(
&self,
py: Python,
Georges Racinet
rust-cpython-revlog: renamed NodeTree import as CoreNodeTree...
r52140 nt: &mut CoreNodeTree,
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 ) -> PyResult<PyObject> {
Georges Racinet
rust-index: using the Rust index in nodemap updating methods
r52099 let index = self.index(py).borrow();
Raphaël Gomès
rust-index: check equality between rust and cindex for `__len__`
r52086 for r in 0..self.len(py)? {
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 let rev = Revision(r as BaseRevision);
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 // in this case node() won't ever return None
nt.insert(&*index, index.node(rev).unwrap(), rev)
.map_err(|e| nodemap_error(py, e))?
}
Ok(py.None())
}
fn get_nodetree<'a>(
&'a self,
py: Python<'a>,
Georges Racinet
rust-cpython-revlog: renamed NodeTree import as CoreNodeTree...
r52140 ) -> PyResult<&'a RefCell<Option<CoreNodeTree>>> {
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 if self.nt(py).borrow().is_none() {
Raphaël Gomès
rust: run a clippy pass with the latest stable version...
r52013 let readonly = Box::<Vec<_>>::default();
Georges Racinet
rust-cpython-revlog: renamed NodeTree import as CoreNodeTree...
r52140 let mut nt = CoreNodeTree::load_bytes(readonly, 0);
Raphaël Gomès
rust-nodemap: use proper Index API instead of using the C API...
r44994 self.fill_nodemap(py, &mut nt)?;
self.nt(py).borrow_mut().replace(nt);
}
Ok(self.nt(py))
}
Georges Racinet
rust-nodemap: add binding for `nodemap_data_all`...
r44995 /// Returns the full nodemap bytes to be written as-is to disk
fn inner_nodemap_data_all(&self, py: Python) -> PyResult<PyBytes> {
let nodemap = self.get_nodetree(py)?.borrow_mut().take().unwrap();
let (readonly, bytes) = nodemap.into_readonly_and_added_bytes();
// If there's anything readonly, we need to build the data again from
// scratch
let bytes = if readonly.len() > 0 {
Georges Racinet
rust-cpython-revlog: renamed NodeTree import as CoreNodeTree...
r52140 let mut nt = CoreNodeTree::load_bytes(Box::<Vec<_>>::default(), 0);
Georges Racinet
rust-nodemap: add binding for `nodemap_data_all`...
r44995 self.fill_nodemap(py, &mut nt)?;
let (readonly, bytes) = nt.into_readonly_and_added_bytes();
assert_eq!(readonly.len(), 0);
bytes
} else {
bytes
};
let bytes = PyBytes::new(py, &bytes);
Ok(bytes)
}
Georges Racinet
rust-nodemap: add binding for `nodemap_data_incremental`...
r44996
/// Returns the last saved docket along with the size of any changed data
/// (in number of blocks), and said data as bytes.
fn inner_nodemap_data_incremental(
&self,
py: Python,
) -> PyResult<PyObject> {
let docket = self.docket(py).borrow();
let docket = match docket.as_ref() {
Some(d) => d,
None => return Ok(py.None()),
};
let node_tree = self.get_nodetree(py)?.borrow_mut().take().unwrap();
let masked_blocks = node_tree.masked_readonly_blocks();
let (_, data) = node_tree.into_readonly_and_added_bytes();
let changed = masked_blocks * std::mem::size_of::<Block>();
Ok((docket, changed, PyBytes::new(py, &data))
.to_py_object(py)
.into_object())
}
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997
/// Update the nodemap from the new (mmaped) data.
/// The docket is kept as a reference for later incremental calls.
fn inner_update_nodemap_data(
&self,
py: Python,
docket: PyObject,
nm_data: PyObject,
) -> PyResult<PyObject> {
Raphaël Gomès
rust-mixed-index: move the mmap keepalive into a function...
r52080 // Safety: we keep the buffer around inside the class as `nodemap_mmap`
let (buf, bytes) = unsafe { mmap_keeparound(py, nm_data)? };
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997 let len = buf.item_count();
Raphaël Gomès
rust-mixed-index: rename variable to make the next change clearer...
r52079 self.nodemap_mmap(py).borrow_mut().replace(buf);
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997
Georges Racinet
rust-cpython-revlog: renamed NodeTree import as CoreNodeTree...
r52140 let mut nt = CoreNodeTree::load_bytes(bytes, len);
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 let data_tip = docket
.getattr(py, "tip_rev")?
.extract::<BaseRevision>(py)?
.into();
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997 self.docket(py).borrow_mut().replace(docket.clone_ref(py));
Georges Racinet
rust-index: using the Rust index in nodemap updating methods
r52099 let idx = self.index(py).borrow();
Raphaël Gomès
rust: use the new `UncheckedRevision` everywhere applicable...
r51870 let data_tip = idx.check_revision(data_tip).ok_or_else(|| {
nodemap_error(py, NodeMapError::RevisionNotInIndex(data_tip))
})?;
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997 let current_tip = idx.len();
Raphaël Gomès
rust: make `Revision` a newtype...
r51872 for r in (data_tip.0 + 1)..current_tip as BaseRevision {
let rev = Revision(r);
Georges Racinet
rust-nodemap: add binding to `nodemap_update_data`...
r44997 // in this case node() won't ever return None
nt.insert(&*idx, idx.node(rev).unwrap(), rev)
.map_err(|e| nodemap_error(py, e))?
}
*self.nt(py).borrow_mut() = Some(nt);
Ok(py.None())
}
Raphaël Gomès
rust-index: implementation of __getitem__...
r52098
fn inner_getitem(&self, py: Python, key: PyObject) -> PyResult<PyObject> {
let idx = self.index(py).borrow();
Ok(match key.extract::<BaseRevision>(py) {
Ok(key_as_int) => {
let entry_params = if key_as_int == NULL_REVISION.0 {
RevisionDataParams::default()
} else {
let rev = UncheckedRevision(key_as_int);
match idx.entry_as_params(rev) {
Some(e) => e,
None => {
return Err(PyErr::new::<IndexError, _>(
py,
"revlog index out of range",
));
}
}
};
revision_data_params_to_py_tuple(py, entry_params)
.into_object()
}
_ => self.get_rev(py, key.extract::<PyBytes>(py)?)?.map_or_else(
|| py.None(),
|py_rev| py_rev.into_py_object(py).into_object(),
),
})
}
Raphaël Gomès
rust-index: implement headrevs
r52108
fn inner_headrevs(&self, py: Python) -> PyResult<PyObject> {
Georges Racinet on incendie.racinet.fr
rust-index: use interior mutability in head revs and caches...
r52127 let index = &*self.index(py).borrow();
Raphaël Gomès
rust-index: implement headrevs
r52108 let as_vec: Vec<PyObject> = index
.head_revs()
.map_err(|e| graph_error(py, e))?
.iter()
.map(|r| PyRevision::from(*r).into_py_object(py).into_object())
.collect();
Ok(PyList::new(py, &as_vec).into_object())
}
Raphaël Gomès
rust-index: add support for `headrevsfiltered`...
r52109
fn inner_headrevsfiltered(
&self,
py: Python,
filtered_revs: &PyObject,
Georges Racinet
rust-index: headrevsfiltered() returning Rust result
r52110 ) -> PyResult<PyObject> {
Raphaël Gomès
rust-index: add support for `headrevsfiltered`...
r52109 let index = &mut *self.index(py).borrow_mut();
let filtered_revs = rev_pyiter_collect(py, filtered_revs, index)?;
Georges Racinet
rust-index: headrevsfiltered() returning Rust result
r52110 let as_vec: Vec<PyObject> = index
Raphaël Gomès
rust-index: add support for `headrevsfiltered`...
r52109 .head_revs_filtered(&filtered_revs)
Georges Racinet
rust-index: headrevsfiltered() returning Rust result
r52110 .map_err(|e| graph_error(py, e))?
.iter()
.map(|r| PyRevision::from(*r).into_py_object(py).into_object())
.collect();
Ok(PyList::new(py, &as_vec).into_object())
Raphaël Gomès
rust-index: add support for `headrevsfiltered`...
r52109 }
Raphaël Gomès
rust-index: add support for `_slicechunktodensity`
r52111
Georges Racinet
rust-index: implement common_ancestors_heads() and ancestors()...
r52118 fn inner_ancestors(
&self,
py: Python,
py_revs: &PyTuple,
) -> PyResult<PyObject> {
Georges Racinet on incendie.racinet.fr
rust-index: use interior mutability in head revs and caches...
r52127 let index = &*self.index(py).borrow();
Georges Racinet
rust-index: implement common_ancestors_heads() and ancestors()...
r52118 let revs: Vec<_> = rev_pyiter_collect(py, py_revs.as_object(), index)?;
let as_vec: Vec<_> = index
.ancestors(&revs)
.map_err(|e| graph_error(py, e))?
.iter()
.map(|r| PyRevision::from(*r).into_py_object(py).into_object())
.collect();
Ok(PyList::new(py, &as_vec).into_object())
}
fn inner_commonancestorsheads(
&self,
py: Python,
py_revs: &PyTuple,
) -> PyResult<PyObject> {
Georges Racinet on incendie.racinet.fr
rust-index: use interior mutability in head revs and caches...
r52127 let index = &*self.index(py).borrow();
Georges Racinet
rust-index: implement common_ancestors_heads() and ancestors()...
r52118 let revs: Vec<_> = rev_pyiter_collect(py, py_revs.as_object(), index)?;
let as_vec: Vec<_> = index
.common_ancestor_heads(&revs)
.map_err(|e| graph_error(py, e))?
.iter()
.map(|r| PyRevision::from(*r).into_py_object(py).into_object())
.collect();
Ok(PyList::new(py, &as_vec).into_object())
}
Raphaël Gomès
rust-index: add support for `computephasesmapsets`...
r52113 fn inner_computephasesmapsets(
&self,
py: Python,
py_roots: PyDict,
) -> PyResult<PyObject> {
let index = &*self.index(py).borrow();
let opt = self.get_nodetree(py)?.borrow();
let nt = opt.as_ref().unwrap();
let roots: Result<HashMap<Phase, Vec<Revision>>, PyErr> = py_roots
.items_list(py)
.iter(py)
.map(|r| {
let phase = r.get_item(py, 0)?;
let nodes = r.get_item(py, 1)?;
// Transform the nodes from Python to revs here since we
// have access to the nodemap
let revs: Result<_, _> = nodes
.iter(py)?
.map(|node| match node?.extract::<PyBytes>(py) {
Ok(py_bytes) => {
let node = node_from_py_bytes(py, &py_bytes)?;
nt.find_bin(index, node.into())
.map_err(|e| nodemap_error(py, e))?
.ok_or_else(|| revlog_error(py))
}
Err(e) => Err(e),
})
.collect();
let phase = Phase::try_from(phase.extract::<usize>(py)?)
.map_err(|_| revlog_error(py));
Ok((phase?, revs?))
})
.collect();
let (len, phase_maps) = index
.compute_phases_map_sets(roots?)
.map_err(|e| graph_error(py, e))?;
// Ugly hack, but temporary
const IDX_TO_PHASE_NUM: [usize; 4] = [1, 2, 32, 96];
let py_phase_maps = PyDict::new(py);
for (idx, roots) in phase_maps.iter().enumerate() {
let phase_num = IDX_TO_PHASE_NUM[idx].into_py_object(py);
// OPTIM too bad we have to collect here. At least, we could
// reuse the same Vec and allocate it with capacity at
// max(len(phase_maps)
let roots_vec: Vec<PyInt> = roots
.iter()
.map(|r| PyRevision::from(*r).into_py_object(py))
.collect();
py_phase_maps.set_item(
py,
phase_num,
PySet::new(py, roots_vec)?,
)?;
}
Ok(PyTuple::new(
py,
&[
len.into_py_object(py).into_object(),
py_phase_maps.into_object(),
],
)
.into_object())
}
Raphaël Gomès
rust-index: add support for `_slicechunktodensity`
r52111 fn inner_slicechunktodensity(
&self,
py: Python,
revs: PyObject,
target_density: f64,
min_gap_size: usize,
Georges Racinet
rust-index: slicechunktodensity returns Rust result...
r52112 ) -> PyResult<PyObject> {
Georges Racinet on incendie.racinet.fr
rust-index: use interior mutability in head revs and caches...
r52127 let index = &*self.index(py).borrow();
Raphaël Gomès
rust-index: add support for `_slicechunktodensity`
r52111 let revs: Vec<_> = rev_pyiter_collect(py, &revs, index)?;
Georges Racinet
rust-index: slicechunktodensity returns Rust result...
r52112 let as_nested_vec =
index.slice_chunk_to_density(&revs, target_density, min_gap_size);
let mut res = Vec::with_capacity(as_nested_vec.len());
let mut py_chunk = Vec::new();
for chunk in as_nested_vec {
py_chunk.clear();
py_chunk.reserve_exact(chunk.len());
for rev in chunk {
py_chunk.push(
PyRevision::from(rev).into_py_object(py).into_object(),
);
}
res.push(PyList::new(py, &py_chunk).into_object());
}
// This is just to do the same as C, not sure why it does this
if res.len() == 1 {
Ok(PyTuple::new(py, &res).into_object())
} else {
Ok(PyList::new(py, &res).into_object())
}
Raphaël Gomès
rust-index: add support for `_slicechunktodensity`
r52111 }
Raphaël Gomès
rust-index: add support for `reachableroots2`...
r52115
fn inner_reachableroots2(
&self,
py: Python,
min_root: UncheckedRevision,
heads: PyObject,
roots: PyObject,
include_path: bool,
) -> PyResult<PyObject> {
let index = &*self.index(py).borrow();
let heads = rev_pyiter_collect_or_else(py, &heads, index, |_rev| {
PyErr::new::<IndexError, _>(py, "head out of range")
})?;
let roots: Result<_, _> = roots
.iter(py)?
.map(|r| {
r.and_then(|o| match o.extract::<PyRevision>(py) {
Ok(r) => Ok(UncheckedRevision(r.0)),
Err(e) => Err(e),
})
})
.collect();
let as_set = index
.reachable_roots(min_root, heads, roots?, include_path)
.map_err(|e| graph_error(py, e))?;
let as_vec: Vec<PyObject> = as_set
.iter()
.map(|r| PyRevision::from(*r).into_py_object(py).into_object())
.collect();
Ok(PyList::new(py, &as_vec).into_object())
}
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 }
Georges Racinet
rust-revlog: bare minimal NodeTree exposition...
r52142 py_class!(pub class NodeTree |py| {
data nt: RefCell<CoreNodeTree>;
data index: RefCell<UnsafePyLeaked<PySharedIndex>>;
def __new__(_cls, index: PyObject) -> PyResult<NodeTree> {
let index = py_rust_index_to_graph(py, index)?;
let nt = CoreNodeTree::default(); // in-RAM, fully mutable
Self::create_instance(py, RefCell::new(nt), RefCell::new(index))
}
Georges Racinet
rust-revlog: add invalidation detection to `NodeTree` class...
r52144 /// Tell whether the NodeTree is still valid
///
/// In case of mutation of the index, the given results are not
/// guaranteed to be correct, and in fact, the methods borrowing
/// the inner index would fail because of `PySharedRef` poisoning
/// (generation-based guard), same as iterating on a `dict` that has
/// been meanwhile mutated.
def is_invalidated(&self) -> PyResult<bool> {
let leaked = self.index(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-revlog: add invalidation detection to `NodeTree` class...
r52144 let result = unsafe { leaked.try_borrow(py) };
// two cases for result to be an error:
// - the index has previously been mutably borrowed
// - there is currently a mutable borrow
// in both cases this amounts for previous results related to
// the index to still be valid.
Ok(result.is_err())
}
Georges Racinet
rust-revlog: bare minimal NodeTree exposition...
r52142 def insert(&self, rev: PyRevision) -> PyResult<PyObject> {
let leaked = self.index(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-revlog: bare minimal NodeTree exposition...
r52142 let index = &*unsafe { leaked.try_borrow(py)? };
let rev = UncheckedRevision(rev.0);
let rev = index
.check_revision(rev)
.ok_or_else(|| rev_not_in_index(py, rev))?;
if rev == NULL_REVISION {
return Err(rev_not_in_index(py, rev.into()))
}
let entry = index.inner.get_entry(rev).unwrap();
let mut nt = self.nt(py).borrow_mut();
nt.insert(index, entry.hash(), rev).map_err(|e| nodemap_error(py, e))?;
Ok(py.None())
}
/// Lookup by node hex prefix in the NodeTree, returning revision number.
///
/// This is not part of the classical NodeTree API, but is good enough
/// for unit testing, as in `test-rust-revlog.py`.
def prefix_rev_lookup(
&self,
node_prefix: PyBytes
) -> PyResult<Option<PyRevision>> {
let prefix = NodePrefix::from_hex(node_prefix.data(py))
.map_err(|_| PyErr::new::<ValueError, _>(
py,
format!("Invalid node or prefix {:?}",
node_prefix.as_object()))
)?;
let nt = self.nt(py).borrow();
let leaked = self.index(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-revlog: bare minimal NodeTree exposition...
r52142 let index = &*unsafe { leaked.try_borrow(py)? };
Ok(nt.find_bin(index, prefix)
.map_err(|e| nodemap_error(py, e))?
.map(|r| r.into())
)
}
def shortest(&self, node: PyBytes) -> PyResult<usize> {
let nt = self.nt(py).borrow();
let leaked = self.index(py).borrow();
Raphaël Gomès
rust-index: document safety invariants being upheld for every `unsafe` block...
r52148 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked`
Georges Racinet
rust-revlog: bare minimal NodeTree exposition...
r52142 let idx = &*unsafe { leaked.try_borrow(py)? };
match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?)
{
Ok(Some(l)) => Ok(l),
Ok(None) => Err(revlog_error(py)),
Err(e) => Err(nodemap_error(py, e)),
}
}
});
Georges Racinet
rust-nodemap: add utils for propagating errors...
r44993 fn revlog_error(py: Python) -> PyErr {
match py
.import("mercurial.error")
.and_then(|m| m.get(py, "RevlogError"))
{
Err(e) => e,
Raphaël Gomès
hg-cpython: fix new occuring TypeError...
r48086 Ok(cls) => PyErr::from_instance(
py,
cls.call(py, (py.None(),), None).ok().into_py_object(py),
),
Georges Racinet
rust-nodemap: add utils for propagating errors...
r44993 }
}
Raphaël Gomès
rust-python-index: don't panic on a corrupted index when calling from Python...
r52124 fn revlog_error_with_msg(py: Python, msg: &[u8]) -> PyErr {
match py
.import("mercurial.error")
.and_then(|m| m.get(py, "RevlogError"))
{
Err(e) => e,
Ok(cls) => PyErr::from_instance(
py,
cls.call(py, (PyBytes::new(py, msg),), None)
.ok()
.into_py_object(py),
),
}
}
Raphaël Gomès
rust-index: implement headrevs
r52108 fn graph_error(py: Python, _err: hg::GraphError) -> PyErr {
// ParentOutOfRange is currently the only alternative
// in `hg::GraphError`. The C index always raises this simple ValueError.
PyErr::new::<ValueError, _>(py, "parent out of range")
}
Georges Racinet
rust-index: renamed nodemap error function for rev not in index...
r52092 fn nodemap_rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
Georges Racinet
rust-nodemap: add utils for propagating errors...
r44993 PyErr::new::<ValueError, _>(
py,
format!(
"Inconsistency: Revision {} found in nodemap \
is not in revlog index",
rev
),
)
}
Georges Racinet
rust-index: helper for revision not in index not involving nodemap...
r52093 fn rev_not_in_index(py: Python, rev: UncheckedRevision) -> PyErr {
PyErr::new::<ValueError, _>(
py,
format!("revlog index out of range: {}", rev),
)
}
Georges Racinet
rust-nodemap: add utils for propagating errors...
r44993 /// Standard treatment of NodeMapError
fn nodemap_error(py: Python, err: NodeMapError) -> PyErr {
match err {
NodeMapError::MultipleResults => revlog_error(py),
Georges Racinet
rust-index: renamed nodemap error function for rev not in index...
r52092 NodeMapError::RevisionNotInIndex(r) => nodemap_rev_not_in_index(py, r),
Georges Racinet
rust-nodemap: add utils for propagating errors...
r44993 }
}
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413 /// Create the module, with __package__ given from parent
pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
let dotted_name = &format!("{}.revlog", package);
let m = PyModule::new(py, dotted_name)?;
m.add(py, "__package__", package)?;
m.add(py, "__doc__", "RevLog - Rust implementations")?;
Georges Racinet on incendie.racinet.fr
rust-index: renamed `MixedIndex` as `Index`...
r52147 m.add_class::<Index>(py)?;
Georges Racinet
rust-revlog: bare minimal NodeTree exposition...
r52142 m.add_class::<NodeTree>(py)?;
Georges Racinet
rust-index: add a struct wrapping the C index...
r44413
let sys = PyModule::import(py, "sys")?;
let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
sys_modules.set_item(py, dotted_name, &m)?;
Ok(m)
}