diff --git a/mercurial/cext/revlog.c b/mercurial/cext/revlog.c --- a/mercurial/cext/revlog.c +++ b/mercurial/cext/revlog.c @@ -2878,6 +2878,12 @@ if (nullentry) PyObject_GC_UnTrack(nullentry); + void *caps = PyCapsule_New( + HgRevlogIndex_GetParents, + "mercurial.cext.parsers.index_get_parents_CAPI", NULL); + if (caps != NULL) + PyModule_AddObject(mod, "index_get_parents_CAPI", caps); + #ifdef WITH_RUST rustlazyancestorsType.tp_new = PyType_GenericNew; if (PyType_Ready(&rustlazyancestorsType) < 0) diff --git a/rust/hg-cpython/src/cindex.rs b/rust/hg-cpython/src/cindex.rs new file mode 100644 --- /dev/null +++ b/rust/hg-cpython/src/cindex.rs @@ -0,0 +1,95 @@ +// cindex.rs +// +// Copyright 2018 Georges Racinet +// +// This software may be used and distributed according to the terms of the +// GNU General Public License version 2 or any later version. + +//! Bindings to use the Index defined by the parsers C extension +//! +//! Ideally, we should use an Index entirely implemented in Rust, +//! but this will take some time to get there. +#[cfg(feature = "python27")] +extern crate python27_sys as python_sys; +#[cfg(feature = "python3")] +extern crate python3_sys as python_sys; + +use self::python_sys::PyCapsule_Import; +use cpython::{PyErr, PyObject, PyResult, Python}; +use hg::{Graph, GraphError, Revision}; +use libc::{c_int, ssize_t}; +use std::ffi::CStr; +use std::mem::transmute; + +type IndexParentsFn = unsafe extern "C" fn( + index: *mut python_sys::PyObject, + 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: PyObject, + parents: IndexParentsFn, +} + +impl Index { + pub fn new(py: Python, index: PyObject) -> PyResult { + Ok(Index { + index: index, + parents: decapsule_parents_fn(py)?, + }) + } +} + +impl Graph for Index { + /// wrap a call to the C extern parents function + fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> { + let mut res: [c_int; 2] = [0; 2]; + let code = unsafe { + (self.parents)( + self.index.as_ptr(), + rev as ssize_t, + &mut res as *mut [c_int; 2], + rev, + ) + }; + match code { + 0 => Ok(res), + _ => Err(GraphError::ParentOutOfRange(rev)), + } + } +} + +/// Return the `index_get_parents` function of the parsers C Extension module. +/// +/// A pointer to the function is stored in the `parsers` module as a +/// standard [Python capsule](https://docs.python.org/2/c-api/capsule.html). +/// +/// This function retrieves the capsule and casts the function pointer +/// +/// Casting function pointers is one of the rare cases of +/// legitimate use cases of `mem::transmute()` (see +/// https://doc.rust-lang.org/std/mem/fn.transmute.html of +/// `mem::transmute()`. +/// It is inappropriate for architectures where +/// function and data pointer sizes differ (so-called "Harvard +/// architectures"), but these are nowadays mostly DSPs +/// and microcontrollers, hence out of our scope. +fn decapsule_parents_fn(py: Python) -> PyResult { + unsafe { + let caps_name = CStr::from_bytes_with_nul_unchecked( + b"mercurial.cext.parsers.index_get_parents_CAPI\0", + ); + let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0); + if from_caps.is_null() { + return Err(PyErr::fetch(py)); + } + Ok(transmute(from_caps)) + } +} diff --git a/rust/hg-cpython/src/lib.rs b/rust/hg-cpython/src/lib.rs --- a/rust/hg-cpython/src/lib.rs +++ b/rust/hg-cpython/src/lib.rs @@ -21,6 +21,7 @@ #[macro_use] extern crate cpython; extern crate hg; +extern crate libc; mod ancestors; mod exceptions;