Mercurial > hg
view rust/hg-direct-ffi/src/ancestors.rs @ 43016:92c8bae84e7a
automation: increase size of Linux AMI build volume
I ran out of space attempting to build a few AMIs. Let's give
the environment an extra 2 GB to utilize.
Differential Revision: https://phab.mercurial-scm.org/D6915
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Sun, 29 Sep 2019 11:06:45 -0700 |
parents | 6a551a2dc666 |
children |
line wrap: on
line source
// 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; extern "C" { fn HgRevlogIndex_GetParents( op: IndexPtr, rev: c_int, parents: *mut [c_int; 2], ) -> 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, } impl Index { pub fn new(index: IndexPtr) -> Self { Index { index: index } } } 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 { HgRevlogIndex_GetParents( self.index, rev, &mut res as *mut [c_int; 2], ) }; match code { 0 => Ok(res), _ => 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, initrevslen: ssize_t, initrevs: *mut c_long, stoprev: c_long, inclusive: c_int, ) -> *mut AncestorsIterator<Index> { assert!(initrevslen >= 0); unsafe { raw_init( Index::new(index), initrevslen as usize, 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 }; let rev = match as_ref.next() { Some(Ok(rev)) => rev, Some(Err(_)) | None => NULL_REVISION, }; rev as c_long } #[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 }; match as_ref.contains(target as Revision) { Ok(r) => r as c_int, Err(_) => -1, } } #[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; 2], 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()); } #[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); } }