revlog: using two new functions in C capsule from Rust code
authorGeorges Racinet <georges.racinet@octobus.net>
Mon, 13 Jan 2020 19:31:33 +0100
changeset 44502 166349510398
parent 44501 87b327de772c
child 44503 887d0f921b34
revlog: using two new functions in C capsule from Rust code We expose `index_length` and `index_node` in the C capsule, so that the Rust representation of the C index can implement the `RevlogIndex` trait. Because our `Node` is actually a one-field struct, we have to decorate it for direct FFI exchange with the C `char*` It would be a good thing to get a length from the C layer, but doing so right now would probably interfere with the upcoming changes that will happen there for the hash length. Differential Revision: https://phab.mercurial-scm.org/D8152
mercurial/cext/revlog.c
rust/hg-core/src/revlog/node.rs
rust/hg-cpython/src/cindex.rs
--- a/mercurial/cext/revlog.c	Tue Feb 18 19:11:14 2020 +0100
+++ b/mercurial/cext/revlog.c	Mon Jan 13 19:31:33 2020 +0100
@@ -39,6 +39,8 @@
 
 typedef struct {
 	int abi_version;
+	Py_ssize_t (*index_length)(const indexObject *);
+	const char *(*index_node)(indexObject *, Py_ssize_t);
 	int (*index_parents)(PyObject *, int, int *);
 } Revlog_CAPI;
 
@@ -2877,7 +2879,9 @@
 static Revlog_CAPI CAPI = {
     /* increment the abi_version field upon each change in the Revlog_CAPI
        struct or in the ABI of the listed functions */
-    1,
+    2,
+    index_length,
+    index_node,
     HgRevlogIndex_GetParents,
 };
 
--- a/rust/hg-core/src/revlog/node.rs	Tue Feb 18 19:11:14 2020 +0100
+++ b/rust/hg-core/src/revlog/node.rs	Mon Jan 13 19:31:33 2020 +0100
@@ -44,6 +44,7 @@
 /// [`nybbles_len`]: #method.nybbles_len
 /// [`ExactLengthRequired`]: struct.NodeError#variant.ExactLengthRequired
 #[derive(Clone, Debug, PartialEq)]
+#[repr(transparent)]
 pub struct Node {
     data: NodeData,
 }
--- a/rust/hg-cpython/src/cindex.rs	Tue Feb 18 19:11:14 2020 +0100
+++ b/rust/hg-cpython/src/cindex.rs	Mon Jan 13 19:31:33 2020 +0100
@@ -11,14 +11,21 @@
 //! but this will take some time to get there.
 
 use cpython::{exc::ImportError, PyClone, PyErr, PyObject, PyResult, Python};
+use hg::revlog::{Node, RevlogIndex};
 use hg::{Graph, GraphError, Revision, WORKING_DIRECTORY_REVISION};
 use libc::c_int;
 
-const REVLOG_CABI_VERSION: c_int = 1;
+const REVLOG_CABI_VERSION: c_int = 2;
 
 #[repr(C)]
 pub struct Revlog_CAPI {
     abi_version: c_int,
+    index_length:
+        unsafe extern "C" fn(index: *mut revlog_capi::RawPyObject) -> c_int,
+    index_node: unsafe extern "C" fn(
+        index: *mut revlog_capi::RawPyObject,
+        rev: c_int,
+    ) -> *const Node,
     index_parents: unsafe extern "C" fn(
         index: *mut revlog_capi::RawPyObject,
         rev: c_int,
@@ -131,3 +138,30 @@
         }
     }
 }
+
+impl RevlogIndex for Index {
+    /// Note C return type is Py_ssize_t (hence signed), but we shall
+    /// force it to unsigned, because it's a length
+    fn len(&self) -> usize {
+        unsafe { (self.capi.index_length)(self.index.as_ptr()) as usize }
+    }
+
+    fn node<'a>(&'a self, rev: Revision) -> Option<&'a Node> {
+        let raw = unsafe {
+            (self.capi.index_node)(self.index.as_ptr(), rev as c_int)
+        };
+        if raw.is_null() {
+            None
+        } else {
+            // TODO it would be much better for the C layer to give us
+            // a length, since the hash length will change in the near
+            // future, but that's probably out of scope for the nodemap
+            // patch series.
+            //
+            // The root of that unsafety relies in the signature of
+            // `capi.index_node()` itself: returning a `Node` pointer
+            // whereas it's a `char *` in the C counterpart.
+            Some(unsafe { &*raw })
+        }
+    }
+}