##// END OF EJS Templates
relnotes: added description about statemod._statecheck...
relnotes: added description about statemod._statecheck Differential Revision: https://phab.mercurial-scm.org/D6557

File last commit:

r40898:443eb4bc default
r42735:df5f6740 default
Show More
ancestors.rs
271 lines | 7.1 KiB | application/rls-services+xml | RustLexer
Georges Racinet
rust: iterator bindings to C code...
r40308 // Copyright 2018 Georges Racinet <gracinet@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;
Yuya Nishihara
rust: look up HgRevlogIndex_GetParents() from symbol table...
r40897
extern "C" {
fn HgRevlogIndex_GetParents(
op: IndexPtr,
rev: c_int,
parents: *mut [c_int; 2],
) -> c_int;
}
Georges Racinet
rust: iterator bindings to C code...
r40308
/// 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,
}
impl Index {
Yuya Nishihara
rust: look up HgRevlogIndex_GetParents() from symbol table...
r40897 pub fn new(index: IndexPtr) -> Self {
Georges Racinet
rust: iterator bindings to C code...
r40308 Index {
index: index,
}
}
}
impl Graph for Index {
/// wrap a call to the C extern parents function
Georges Racinet
rust: changed Graph.parents to return [Revision; 2]...
r40969 fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> {
Georges Racinet
rust: iterator bindings to C code...
r40308 let mut res: [c_int; 2] = [0; 2];
let code =
Yuya Nishihara
rust: look up HgRevlogIndex_GetParents() from symbol table...
r40897 unsafe { HgRevlogIndex_GetParents(self.index, rev, &mut res as *mut [c_int; 2]) };
Georges Racinet
rust: iterator bindings to C code...
r40308 match code {
Georges Racinet
rust: changed Graph.parents to return [Revision; 2]...
r40969 0 => Ok(res),
Georges Racinet
rust: iterator bindings to C code...
r40308 _ => 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,
Yuya Nishihara
rust: fix signature of rustlazyancestors_init() function...
r40493 initrevslen: ssize_t,
Georges Racinet
rust: iterator bindings to C code...
r40308 initrevs: *mut c_long,
stoprev: c_long,
inclusive: c_int,
) -> *mut AncestorsIterator<Index> {
Yuya Nishihara
rust: fix signature of rustlazyancestors_init() function...
r40493 assert!(initrevslen >= 0);
Georges Racinet
rust: iterator bindings to C code...
r40308 unsafe {
raw_init(
Yuya Nishihara
rust: look up HgRevlogIndex_GetParents() from symbol table...
r40897 Index::new(index),
Yuya Nishihara
rust: fix signature of rustlazyancestors_init() function...
r40493 initrevslen as usize,
Georges Racinet
rust: iterator bindings to C code...
r40308 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 };
Yuya Nishihara
rust: propagate error of index_get_parents() properly...
r40898 let rev = match as_ref.next() {
Some(Ok(rev)) => rev,
Some(Err(_)) | None => NULL_REVISION,
};
rev as c_long
Georges Racinet
rust: iterator bindings to C code...
r40308 }
Georges Racinet
rust: rustlazyancestors.__contains__...
r40336 #[no_mangle]
pub extern "C" fn rustlazyancestors_contains(
raw: *mut AncestorsIterator<Index>,
target: c_long,
) -> c_int {
raw_contains(raw, target)
}
/// Testable (for any Graph) version of rustlazayancestors_next
#[inline]
fn raw_contains<G: Graph>(
raw: *mut AncestorsIterator<G>,
target: c_long,
) -> c_int {
let as_ref = unsafe { &mut *raw };
Yuya Nishihara
rust: propagate error of index_get_parents() properly...
r40898 match as_ref.contains(target as Revision) {
Ok(r) => r as c_int,
Err(_) => -1,
Georges Racinet
rust: rustlazyancestors.__contains__...
r40336 }
}
Georges Racinet
rust: iterator bindings to C code...
r40308 #[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[derive(Clone, Debug)]
struct Stub;
impl Graph for Stub {
Georges Racinet
rust: changed Graph.parents to return [Revision; 2]...
r40969 fn parents(&self, r: Revision) -> Result<[Revision; 2], GraphError> {
Georges Racinet
rust: iterator bindings to C code...
r40308 match r {
25 => Err(GraphError::ParentOutOfRange(25)),
Georges Racinet
rust: changed Graph.parents to return [Revision; 2]...
r40969 _ => Ok([1, 2]),
Georges Racinet
rust: iterator bindings to C code...
r40308 }
}
}
/// 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());
}
Georges Racinet
rust: rustlazyancestors.__contains__...
r40336
#[test]
fn test_contains() {
let raw = stub_raw_init_from_vec(vec![5, 6], 0, 1);
assert_eq!(raw_contains(raw, 5), 1);
assert_eq!(raw_contains(raw, 2), 1);
}
#[test]
fn test_contains_exclusive() {
let raw = stub_raw_init_from_vec(vec![5, 6], 0, 0);
assert_eq!(raw_contains(raw, 5), 0);
assert_eq!(raw_contains(raw, 2), 1);
}
Georges Racinet
rust: iterator bindings to C code...
r40308 }