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 | fn revlog_error(py: Python) -> PyErr { |
|
1035 | fn revlog_error(py: Python) -> PyErr { | |
969 | match py |
|
1036 | match py | |
970 | .import("mercurial.error") |
|
1037 | .import("mercurial.error") | |
@@ -1033,6 +1100,7 b' pub fn init_module(py: Python, package: ' | |||||
1033 | m.add(py, "__doc__", "RevLog - Rust implementations")?; |
|
1100 | m.add(py, "__doc__", "RevLog - Rust implementations")?; | |
1034 |
|
1101 | |||
1035 | m.add_class::<MixedIndex>(py)?; |
|
1102 | m.add_class::<MixedIndex>(py)?; | |
|
1103 | m.add_class::<NodeTree>(py)?; | |||
1036 |
|
1104 | |||
1037 | let sys = PyModule::import(py, "sys")?; |
|
1105 | let sys = PyModule::import(py, "sys")?; | |
1038 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
|
1106 | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; |
@@ -1,6 +1,8 b'' | |||||
1 | import struct |
|
1 | import struct | |
2 | import unittest |
|
2 | import unittest | |
3 |
|
3 | |||
|
4 | from mercurial.node import hex | |||
|
5 | ||||
4 | try: |
|
6 | try: | |
5 | from mercurial import rustext |
|
7 | from mercurial import rustext | |
6 |
|
8 | |||
@@ -57,6 +59,35 b' class RustRevlogIndexTest(revlogtesting.' | |||||
57 | self.assertFalse(LazyAncestors(rustidx, [0], 0, False)) |
|
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 | if __name__ == '__main__': |
|
91 | if __name__ == '__main__': | |
61 | import silenttestrunner |
|
92 | import silenttestrunner | |
62 |
|
93 |
General Comments 0
You need to be logged in to leave comments.
Login now