yutannihilation / savvy

A simple R extension interface using Rust
https://yutannihilation.github.io/savvy/guide/
MIT License
63 stars 3 forks source link

ALTREP #206

Closed yutannihilation closed 4 months ago

yutannihilation commented 4 months ago

Note that, this is a toy implementation. Probably, for more serious usage, savvy needs to support utilizing data2 more nicely.

use savvy::savvy;

struct MyAltInt(Vec<i32>);
impl savvy::IntoExtPtrSexp for MyAltInt {}

impl MyAltInt {
    fn new(x: Vec<i32>) -> Self {
        Self(x)
    }
}

impl AltInteger for MyAltInt {
    const CLASS_NAME: &'static str = "MyAltInt";
    const PACKAGE_NAME: &'static str = "TestPackage";

    fn length(&mut self) -> usize {
        self.0.len()
    }

    fn elt(&mut self, i: usize) -> i32 {
        self.0[i]
    }
}

// A user-visible function
#[savvy]
fn altint() -> savvy::Result<savvy::Sexp> {
    let v = MyAltInt::new(vec![1, 2, 3]);
    let v_altrep = v.into_altrep()?;
    Ok(savvy::Sexp(v_altrep))
}

// A function for initialization
#[savvy]
fn init_altrep_class(dll_info: *mut savvy::ffi::DllInfo) -> savvy::Result<()> {
    register_altinteger_class::<MyAltInt>(dll_info)?;
    Ok(())
}
yutannihilation commented 4 months ago

Limitation at the current prototyping phase: #[savvy_init] simply exposes and executes the function as it is. It's user's responsibility to make sure the function is callable from C. No error can be emitted. Probably, #[savvy_init] should generate the same wrapper as #[savvy], or, rather, #[savvy] should automatically detect the initialization function by checking if DllInfo is used.

yutannihilation commented 4 months ago

Example implementation: https://github.com/yutannihilation/savvy-altrep-test

use savvy::altrep::{register_altinteger_class, AltInteger};
use savvy::{savvy, savvy_init};

struct MyAltInt(Vec<i32>);
impl savvy::IntoExtPtrSexp for MyAltInt {}

impl MyAltInt {
    fn new(x: Vec<i32>) -> Self {
        Self(x)
    }
}

impl AltInteger for MyAltInt {
    const CLASS_NAME: &'static str = "MyAltInt";

    fn length(&mut self) -> usize {
        self.0.len()
    }

    fn elt(&mut self, i: usize) -> i32 {
        self.0[i]
    }
}

#[allow(clippy::missing_safety_doc)]
#[no_mangle]
#[savvy_init]
pub unsafe extern "C" fn init_altrep_class(dll_info: *mut savvy::ffi::DllInfo) {
    register_altinteger_class::<MyAltInt>(dll_info);
}

#[savvy]
fn altint() -> savvy::Result<savvy::Sexp> {
    let v = MyAltInt::new(vec![1, 2, 3]);
    let v_altrep = savvy::altrep::create_altrep_instance(v)?;
    Ok(savvy::Sexp(v_altrep))
}