[PATCH 2 of 5 V3] rust: iterator bindings to C code
Georges Racinet
gracinet at anybox.fr
Sat Oct 13 09:59:40 EDT 2018
# HG changeset patch
# User Georges Racinet <gracinet at anybox.fr>
# Date 1538059896 -7200
# Thu Sep 27 16:51:36 2018 +0200
# Node ID 4b490e500a3551b03c41dc06f16aa506523719c6
# Parent 41b32e06629466bbe671c6ebfc1c5a4133c8af99
# EXP-Topic rustancestors-contains
rust: iterator bindings to C code
In this changeset, still made of Rust code only,
we expose the Rust iterator for instantiation and
consumption from C code.
The idea is that both the index and index_get_parents()
will be passed from the C extension, hence avoiding a hard
link dependency to parsers.so, so that the crate can
still be built and tested independently.
On the other hand, parsers.so will use the symbols
defined in this changeset.
diff -r 41b32e066294 -r 4b490e500a35 rust/Cargo.lock
--- a/rust/Cargo.lock Thu Sep 27 17:03:16 2018 +0200
+++ b/rust/Cargo.lock Thu Sep 27 16:51:36 2018 +0200
@@ -30,6 +30,14 @@
]
[[package]]
+name = "hgdirectffi"
+version = "0.1.0"
+dependencies = [
+ "hg-core 0.1.0",
+ "libc 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff -r 41b32e066294 -r 4b490e500a35 rust/Cargo.toml
--- a/rust/Cargo.toml Thu Sep 27 17:03:16 2018 +0200
+++ b/rust/Cargo.toml Thu Sep 27 16:51:36 2018 +0200
@@ -1,3 +1,3 @@
[workspace]
-members = ["hgcli", "hg-core"]
+members = ["hgcli", "hg-core", "hg-direct-ffi"]
exclude = ["chg"]
diff -r 41b32e066294 -r 4b490e500a35 rust/hg-direct-ffi/Cargo.toml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-direct-ffi/Cargo.toml Thu Sep 27 16:51:36 2018 +0200
@@ -0,0 +1,12 @@
+[package]
+name = "hgdirectffi"
+version = "0.1.0"
+authors = ["Georges Racinet <gracinet at anybox.fr>"]
+description = "Low level Python bindings for hg-core, going through existing C extensions"
+
+[dependencies]
+libc = "*"
+hg-core = { path = "../hg-core" }
+
+[lib]
+crate-type = ["staticlib"]
diff -r 41b32e066294 -r 4b490e500a35 rust/hg-direct-ffi/src/ancestors.rs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-direct-ffi/src/ancestors.rs Thu Sep 27 16:51:36 2018 +0200
@@ -0,0 +1,229 @@
+// Copyright 2018 Georges Racinet <gracinet at anybox.fr>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Bindings for CPython extension code
+//!
+//! This exposes methods to build and use a `rustlazyancestors` iterator
+//! from C code, using an index and its parents function that are passed
+//! from the caller at instantiation.
+
+use hg::AncestorsIterator;
+use hg::{Graph, GraphError, Revision, NULL_REVISION};
+use libc::{c_int, c_long, c_void, ssize_t};
+use std::ptr::null_mut;
+use std::slice;
+
+type IndexPtr = *mut c_void;
+type IndexParentsFn =
+ unsafe extern "C" fn(index: IndexPtr, rev: ssize_t, ps: *mut [c_int; 2], max_rev: c_int)
+ -> c_int;
+
+/// A Graph backed up by objects and functions from revlog.c
+///
+/// This implementation of the Graph trait, relies on (pointers to)
+/// - the C index object (`index` member)
+/// - the `index_get_parents()` function (`parents` member)
+pub struct Index {
+ index: IndexPtr,
+ parents: IndexParentsFn,
+}
+
+impl Index {
+ pub fn new(index: IndexPtr, parents: IndexParentsFn) -> Self {
+ Index {
+ index: index,
+ parents: parents,
+ }
+ }
+}
+
+impl Graph for Index {
+ /// wrap a call to the C extern parents function
+ fn parents(&self, rev: Revision) -> Result<(Revision, Revision), GraphError> {
+ let mut res: [c_int; 2] = [0; 2];
+ let code =
+ unsafe { (self.parents)(self.index, rev as ssize_t, &mut res as *mut [c_int; 2], rev) };
+ match code {
+ 0 => Ok((res[0], res[1])),
+ _ => Err(GraphError::ParentOutOfRange(rev)),
+ }
+ }
+}
+
+/// Wrapping of AncestorsIterator<Index> constructor, for C callers.
+///
+/// Besides `initrevs`, `stoprev` and `inclusive`, that are converted
+/// we receive the index and the parents function as pointers
+#[no_mangle]
+pub extern "C" fn rustlazyancestors_init(
+ index: IndexPtr,
+ parents: IndexParentsFn,
+ initrevslen: usize,
+ initrevs: *mut c_long,
+ stoprev: c_long,
+ inclusive: c_int,
+) -> *mut AncestorsIterator<Index> {
+ unsafe {
+ raw_init(
+ Index::new(index, parents),
+ initrevslen,
+ initrevs,
+ stoprev,
+ inclusive,
+ )
+ }
+}
+
+/// Testable (for any Graph) version of rustlazyancestors_init
+#[inline]
+unsafe fn raw_init<G: Graph>(
+ graph: G,
+ initrevslen: usize,
+ initrevs: *mut c_long,
+ stoprev: c_long,
+ inclusive: c_int,
+) -> *mut AncestorsIterator<G> {
+ let inclb = match inclusive {
+ 0 => false,
+ 1 => true,
+ _ => {
+ return null_mut();
+ }
+ };
+
+ let slice = slice::from_raw_parts(initrevs, initrevslen);
+
+ Box::into_raw(Box::new(match AncestorsIterator::new(
+ graph,
+ slice.into_iter().map(|&r| r as Revision),
+ stoprev as Revision,
+ inclb,
+ ) {
+ Ok(it) => it,
+ Err(_) => {
+ return null_mut();
+ }
+ }))
+}
+
+/// Deallocator to be called from C code
+#[no_mangle]
+pub extern "C" fn rustlazyancestors_drop(raw_iter: *mut AncestorsIterator<Index>) {
+ raw_drop(raw_iter);
+}
+
+/// Testable (for any Graph) version of rustlazayancestors_drop
+#[inline]
+fn raw_drop<G: Graph>(raw_iter: *mut AncestorsIterator<G>) {
+ unsafe {
+ Box::from_raw(raw_iter);
+ }
+}
+
+/// Iteration main method to be called from C code
+///
+/// We convert the end of iteration into NULL_REVISION,
+/// it will be up to the C wrapper to convert that back into a Python end of
+/// iteration
+#[no_mangle]
+pub extern "C" fn rustlazyancestors_next(raw: *mut AncestorsIterator<Index>) -> c_long {
+ raw_next(raw)
+}
+
+/// Testable (for any Graph) version of rustlazayancestors_next
+#[inline]
+fn raw_next<G: Graph>(raw: *mut AncestorsIterator<G>) -> c_long {
+ let as_ref = unsafe { &mut *raw };
+ as_ref.next().unwrap_or(NULL_REVISION) as c_long
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::thread;
+
+ #[derive(Clone, Debug)]
+ struct Stub;
+
+ impl Graph for Stub {
+ fn parents(&self, r: Revision) -> Result<(Revision, Revision), GraphError> {
+ match r {
+ 25 => Err(GraphError::ParentOutOfRange(25)),
+ _ => Ok((1, 2)),
+ }
+ }
+ }
+
+ /// Helper for test_init_next()
+ fn stub_raw_init(
+ initrevslen: usize,
+ initrevs: usize,
+ stoprev: c_long,
+ inclusive: c_int,
+ ) -> usize {
+ unsafe {
+ raw_init(
+ Stub,
+ initrevslen,
+ initrevs as *mut c_long,
+ stoprev,
+ inclusive,
+ ) as usize
+ }
+ }
+
+ fn stub_raw_init_from_vec(
+ mut initrevs: Vec<c_long>,
+ stoprev: c_long,
+ inclusive: c_int,
+ ) -> *mut AncestorsIterator<Stub> {
+ unsafe {
+ raw_init(
+ Stub,
+ initrevs.len(),
+ initrevs.as_mut_ptr(),
+ stoprev,
+ inclusive,
+ )
+ }
+ }
+
+ #[test]
+ // Test what happens when we init an Iterator as with the exposed C ABI
+ // and try to use it afterwards
+ // We spawn new threads, in order to make memory consistency harder
+ // but this forces us to convert the pointers into shareable usizes.
+ fn test_init_next() {
+ let mut initrevs: Vec<c_long> = vec![11, 13];
+ let initrevs_len = initrevs.len();
+ let initrevs_ptr = initrevs.as_mut_ptr() as usize;
+ let handler = thread::spawn(move || stub_raw_init(initrevs_len, initrevs_ptr, 0, 1));
+ let raw = handler.join().unwrap() as *mut AncestorsIterator<Stub>;
+
+ assert_eq!(raw_next(raw), 13);
+ assert_eq!(raw_next(raw), 11);
+ assert_eq!(raw_next(raw), 2);
+ assert_eq!(raw_next(raw), 1);
+ assert_eq!(raw_next(raw), NULL_REVISION as c_long);
+ raw_drop(raw);
+ }
+
+ #[test]
+ fn test_init_wrong_bool() {
+ assert_eq!(stub_raw_init_from_vec(vec![11, 13], 0, 2), null_mut());
+ }
+
+ #[test]
+ fn test_empty() {
+ let raw = stub_raw_init_from_vec(vec![], 0, 1);
+ assert_eq!(raw_next(raw), NULL_REVISION as c_long);
+ raw_drop(raw);
+ }
+
+ #[test]
+ fn test_init_err_out_of_range() {
+ assert!(stub_raw_init_from_vec(vec![25], 0, 0).is_null());
+ }
+}
diff -r 41b32e066294 -r 4b490e500a35 rust/hg-direct-ffi/src/lib.rs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-direct-ffi/src/lib.rs Thu Sep 27 16:51:36 2018 +0200
@@ -0,0 +1,16 @@
+// Copyright 2018 Georges Racinet <gracinet at anybox.fr>
+//
+// This software may be used and distributed according to the terms of the
+// GNU General Public License version 2 or any later version.
+
+//! Bindings for CPython extension code
+//!
+//! This exposes methods to build and use a `rustlazyancestors` iterator
+//! from C code, using an index and its parents function that are passed
+//! from the caller at instantiation.
+
+extern crate hg;
+extern crate libc;
+
+mod ancestors;
+pub use ancestors::{rustlazyancestors_drop, rustlazyancestors_init, rustlazyancestors_next};
More information about the Mercurial-devel
mailing list