Show More
@@ -965,6 +965,73 b' impl MixedIndex {' | |||
|
965 | 965 | } |
|
966 | 966 | } |
|
967 | 967 | |
|
968 | py_class!(pub class NodeTree |py| { | |
|
969 | data nt: RefCell<CoreNodeTree>; | |
|
970 | data index: RefCell<UnsafePyLeaked<PySharedIndex>>; | |
|
971 | ||
|
972 | def __new__(_cls, index: PyObject) -> PyResult<NodeTree> { | |
|
973 | let index = py_rust_index_to_graph(py, index)?; | |
|
974 | let nt = CoreNodeTree::default(); // in-RAM, fully mutable | |
|
975 | Self::create_instance(py, RefCell::new(nt), RefCell::new(index)) | |
|
976 | } | |
|
977 | ||
|
978 | def insert(&self, rev: PyRevision) -> PyResult<PyObject> { | |
|
979 | let leaked = self.index(py).borrow(); | |
|
980 | let index = &*unsafe { leaked.try_borrow(py)? }; | |
|
981 | ||
|
982 | let rev = UncheckedRevision(rev.0); | |
|
983 | let rev = index | |
|
984 | .check_revision(rev) | |
|
985 | .ok_or_else(|| rev_not_in_index(py, rev))?; | |
|
986 | if rev == NULL_REVISION { | |
|
987 | return Err(rev_not_in_index(py, rev.into())) | |
|
988 | } | |
|
989 | ||
|
990 | let entry = index.inner.get_entry(rev).unwrap(); | |
|
991 | let mut nt = self.nt(py).borrow_mut(); | |
|
992 | nt.insert(index, entry.hash(), rev).map_err(|e| nodemap_error(py, e))?; | |
|
993 | ||
|
994 | Ok(py.None()) | |
|
995 | } | |
|
996 | ||
|
997 | /// Lookup by node hex prefix in the NodeTree, returning revision number. | |
|
998 | /// | |
|
999 | /// This is not part of the classical NodeTree API, but is good enough | |
|
1000 | /// for unit testing, as in `test-rust-revlog.py`. | |
|
1001 | def prefix_rev_lookup( | |
|
1002 | &self, | |
|
1003 | node_prefix: PyBytes | |
|
1004 | ) -> PyResult<Option<PyRevision>> { | |
|
1005 | let prefix = NodePrefix::from_hex(node_prefix.data(py)) | |
|
1006 | .map_err(|_| PyErr::new::<ValueError, _>( | |
|
1007 | py, | |
|
1008 | format!("Invalid node or prefix {:?}", | |
|
1009 | node_prefix.as_object())) | |
|
1010 | )?; | |
|
1011 | ||
|
1012 | let nt = self.nt(py).borrow(); | |
|
1013 | let leaked = self.index(py).borrow(); | |
|
1014 | let index = &*unsafe { leaked.try_borrow(py)? }; | |
|
1015 | ||
|
1016 | Ok(nt.find_bin(index, prefix) | |
|
1017 | .map_err(|e| nodemap_error(py, e))? | |
|
1018 | .map(|r| r.into()) | |
|
1019 | ) | |
|
1020 | } | |
|
1021 | ||
|
1022 | def shortest(&self, node: PyBytes) -> PyResult<usize> { | |
|
1023 | let nt = self.nt(py).borrow(); | |
|
1024 | let leaked = self.index(py).borrow(); | |
|
1025 | let idx = &*unsafe { leaked.try_borrow(py)? }; | |
|
1026 | match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?) | |
|
1027 | { | |
|
1028 | Ok(Some(l)) => Ok(l), | |
|
1029 | Ok(None) => Err(revlog_error(py)), | |
|
1030 | Err(e) => Err(nodemap_error(py, e)), | |
|
1031 | } | |
|
1032 | } | |
|
1033 | }); | |
|
1034 | ||
|
968 | 1035 | fn revlog_error(py: Python) -> PyErr { |
|
969 | 1036 | match py |
|
970 | 1037 | .import("mercurial.error") |
@@ -1033,6 +1100,7 b' pub fn init_module(py: Python, package: ' | |||
|
1033 | 1100 | m.add(py, "__doc__", "RevLog - Rust implementations")?; |
|
1034 | 1101 | |
|
1035 | 1102 | m.add_class::<MixedIndex>(py)?; |
|
1103 | m.add_class::<NodeTree>(py)?; | |
|
1036 | 1104 | |
|
1037 | 1105 | let sys = PyModule::import(py, "sys")?; |
|
1038 | 1106 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
@@ -1,6 +1,8 b'' | |||
|
1 | 1 | import struct |
|
2 | 2 | import unittest |
|
3 | 3 | |
|
4 | from mercurial.node import hex | |
|
5 | ||
|
4 | 6 | try: |
|
5 | 7 | from mercurial import rustext |
|
6 | 8 | |
@@ -57,6 +59,35 b' class RustRevlogIndexTest(revlogtesting.' | |||
|
57 | 59 | self.assertFalse(LazyAncestors(rustidx, [0], 0, False)) |
|
58 | 60 | |
|
59 | 61 | |
|
62 | @unittest.skipIf( | |
|
63 | rustext is None, | |
|
64 | "rustext module revlog relies on is not available", | |
|
65 | ) | |
|
66 | class RustRevlogNodeTreeClassTest(revlogtesting.RustRevlogBasedTestBase): | |
|
67 | def test_standalone_nodetree(self): | |
|
68 | idx = self.parserustindex() | |
|
69 | nt = revlog.NodeTree(idx) | |
|
70 | for i in range(4): | |
|
71 | nt.insert(i) | |
|
72 | ||
|
73 | bin_nodes = [entry[7] for entry in idx] | |
|
74 | hex_nodes = [hex(n) for n in bin_nodes] | |
|
75 | ||
|
76 | for i, node in enumerate(hex_nodes): | |
|
77 | self.assertEqual(nt.prefix_rev_lookup(node), i) | |
|
78 | self.assertEqual(nt.prefix_rev_lookup(node[:5]), i) | |
|
79 | ||
|
80 | # all 4 revisions in idx (standard data set) have different | |
|
81 | # first nybbles in their Node IDs, | |
|
82 | # hence `nt.shortest()` should return 1 for them, except when | |
|
83 | # the leading nybble is 0 (ambiguity with NULL_NODE) | |
|
84 | for i, (bin_node, hex_node) in enumerate(zip(bin_nodes, hex_nodes)): | |
|
85 | shortest = nt.shortest(bin_node) | |
|
86 | expected = 2 if hex_node[0] == ord('0') else 1 | |
|
87 | self.assertEqual(shortest, expected) | |
|
88 | self.assertEqual(nt.prefix_rev_lookup(hex_node[:shortest]), i) | |
|
89 | ||
|
90 | ||
|
60 | 91 | if __name__ == '__main__': |
|
61 | 92 | import silenttestrunner |
|
62 | 93 |
General Comments 0
You need to be logged in to leave comments.
Login now