Crate rmin

source ·
Expand description

§rmin - A minimal Rust lib for writting R extensions

This is a very early version, only support vector type, and thus its overhead is minimized.

Compare to the well-knowned rextendr, This crate although with some limitations but could provide a faster implementation, a smaller code size, and a faster compile time ( could generate a release build in 0.45s with `#![no_std], but may cause memory leak).

Since it is small enough, you could vendor this crate easily into your CRAN package.

§Usage

Version 0.1.0 provides a fastest (but ugly) way to achieve about 2x speedup on with functions. They are discarded in 0.2.* since they are really unsafe and may cause memory leak.

I cannot ensure whether the api will change again in the future, but currently v0.2.0 seems usable.

§0.2.0, rewrite to prevent memory leak

Several changes have been made since v0.1.0:

  1. Add a panic_handler with default std feature, it is because currently, Rust cannot call all drop function before a longjmp is executed, which will cause memory leak, thus catch_unwind must be used and thus std crate is needed. I have not check whether #![no_std] works now, but it is better not to use it.
  2. Move SEXP to libR::SEXP, and exporting SEXP<T>, Owned<T> (and Protected<T> generated by Owned<T>::protect which cannot be used directly, since return Protected<T> to R will break the protect balance)
  3. Currently, you could directly indexing a sexp object, but convert them firstly into slices is more preferred.

§grammar

use rmin::prelude::*;
/// Return a+b to R.
#[no_mangle]
pub extern "C-unwind" fn add_protect(a:SEXP<f64>,b:SEXP<f64>) -> Owned<f64> {
    handle_panic(||{
        let mut c=Owned::new(1).protect();
        c[0]=a[0]+b[0];
        c.into()
    })
}
#[no_mangle]
pub extern "C-unwind" fn add_noprotect(a:SEXP<f64>,b:SEXP<f64>) -> Owned<f64> {
    handle_panic(||{
        let mut c=Owned::new(1);
        c[0]=a[0]+b[0];
        c
    })
}

/// raise panic.
#[no_mangle]
pub extern "C-unwind" fn panic() -> Owned<f64> {
    handle_panic(||{
        panic!("error occurs")
    })
}

The program above could be tested with test command

export LOAD="dyn.load('target/release/examples/libcompare_rmin.so');addnp=getNativeSymbolInfo('add_noprotect');addp=getNativeSymbolInfo('add_protect');panic=getNativeSymbolInfo('panic')" ; LC_ALL=C r -e "$LOAD;system.time(sapply(1:100000,function(x)tryCatch(.Call(wrap__panic),error=I)))" 2>/dev/null ; LC_ALL=C r -e "$LOAD;system.time(sapply(1:1000000,function(x).Call(addnp,1.,2.)));system.time(sapply(1:1000000,function(x).Call(addnp,1.,2.)))"

Modules§

  • It makes no difference to choose either write use rmin::prelude::* or use rmin::*.

Structs§

  • Owned SEXP, allocated by Rust code.
  • ReadOnly SEXP, should not be changed.

Traits§

Functions§

  • The most common function that may use close to the FFI boundary. This function could catch all possible panic and convert them into normal R error message.