[PATCH 2 of 6 V2] rust: iterator bindings to C code
Georges Racinet
gracinet at anybox.fr
Tue Oct 9 11:22:46 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 81b8781de6fad514634713fa2cb9f10c320d1af3
# Parent e466b892b487a2f258fe2000c76a9e916c7344d8
# EXP-Topic rustancestors-rfc
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 e466b892b487 -r 81b8781de6fa mercurial/rust/Cargo.lock
--- a/mercurial/rust/Cargo.lock Thu Sep 27 17:03:16 2018 +0200
+++ b/mercurial/rust/Cargo.lock Thu Sep 27 16:51:36 2018 +0200
@@ -1,4 +1,14 @@
[[package]]
name = "hgancestors"
version = "0.1.0"
+dependencies = [
+ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+[[package]]
+name = "libc"
+version = "0.2.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
diff -r e466b892b487 -r 81b8781de6fa mercurial/rust/Cargo.toml
--- a/mercurial/rust/Cargo.toml Thu Sep 27 17:03:16 2018 +0200
+++ b/mercurial/rust/Cargo.toml Thu Sep 27 16:51:36 2018 +0200
@@ -2,3 +2,9 @@
name = "hgancestors"
version = "0.1.0"
authors = ["Georges Racinet <gracinet at anybox.fr>"]
+
+[dependencies]
+libc = "*"
+
+[lib]
+crate-type = ["dylib"]
diff -r e466b892b487 -r 81b8781de6fa mercurial/rust/src/cpython.rs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/rust/src/cpython.rs Thu Sep 27 16:51:36 2018 +0200
@@ -0,0 +1,240 @@
+// 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 libc;
+
+use self::libc::{c_void, c_int, c_long, ssize_t};
+use std::ptr::null_mut;
+use std::slice;
+use super::{Graph, GraphError, Revision, NULL_REVISION};
+use super::ancestors::AncestorsIterator;
+
+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_long,
+) -> *mut AncestorsIterator<Index> {
+
+ raw_init(
+ Index::new(index, parents),
+ initrevslen,
+ initrevs,
+ stoprev,
+ inclusive,
+ )
+}
+
+/// Testable (for any Graph) version of rustlazyancestors_init
+#[inline]
+fn raw_init<G: Graph>(
+ graph: G,
+ initrevslen: usize,
+ initrevs: *mut c_long,
+ stoprev: c_long,
+ inclusive: c_long,
+) -> *mut AncestorsIterator<G> {
+
+ let inclb = match inclusive {
+ 0 => false,
+ 1 => true,
+ _ => {
+ return null_mut();
+ }
+ };
+
+ let slice = unsafe { 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 std::thread;
+ use super::*;
+
+ #[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_long,
+ ) -> usize {
+ raw_init(
+ Stub,
+ initrevslen,
+ initrevs as *mut c_long,
+ stoprev,
+ inclusive,
+ ) as usize
+ }
+
+ #[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() {
+ let mut initrevs: Vec<c_long> = vec![11, 13];
+ let raw = raw_init(Stub, initrevs.len(), initrevs.as_mut_ptr(), 0, 2);
+ assert_eq!(raw, null_mut());
+ }
+
+ #[test]
+ fn test_empty() {
+ let mut initrevs: Vec<c_long> = vec![];
+ let raw = raw_init(Stub, initrevs.len(), initrevs.as_mut_ptr(), 0, 1);
+ assert_eq!(raw_next(raw), NULL_REVISION as c_long);
+ raw_drop(raw);
+ }
+
+ #[test]
+ fn test_init_err_out_of_range() {
+ let mut initrevs: Vec<c_long> = vec![25];
+ assert!(
+ raw_init(Stub, initrevs.len(), initrevs.as_mut_ptr(), 0, 0)
+ .is_null()
+ );
+ }
+}
diff -r e466b892b487 -r 81b8781de6fa mercurial/rust/src/lib.rs
--- a/mercurial/rust/src/lib.rs Thu Sep 27 17:03:16 2018 +0200
+++ b/mercurial/rust/src/lib.rs Thu Sep 27 16:51:36 2018 +0200
@@ -3,7 +3,10 @@
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
mod ancestors;
-pub use ancestors::AncestorsIterator;
+
+mod cpython;
+pub use cpython::{rustlazyancestors_init, rustlazyancestors_drop,
+ rustlazyancestors_next};
/// Mercurial revision numbers
///
More information about the Mercurial-devel
mailing list