Skip to content

Commit

Permalink
ALTREP (#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
yutannihilation authored Apr 29, 2024
1 parent 8ba2643 commit 0cbaf98
Show file tree
Hide file tree
Showing 21 changed files with 1,264 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ jobs:
crate-type = ["staticlib"]
[dependencies]
savvy = { path = "../savvy", features = ["complex"] }
savvy = { path = "../savvy", features = ["complex", "altrep"] }
savvy-ffi = { path = "../savvy/savvy-ffi" }
[workspace.package]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/wasm_and_arm64.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
crate-type = ["staticlib"]
[dependencies]
savvy = { path = "../savvy", features = ["complex"] }
savvy = { path = "../savvy", features = ["complex", "altrep"] }
savvy-ffi = { path = "../savvy/savvy-ffi" }
[workspace.package]
Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ default = []
# Support complex
complex = ["num-complex", "savvy-ffi/complex"]

# Support ALTREP
altrep = ["savvy-ffi/altrep"]

[build-dependencies]
cc = "1"

[package.metadata.docs.rs]
features = ["complex"]
features = ["complex", "altrep"]

[workspace.metadata.release]
tag = false # do not create tags for individual crates (e.g. "savvy-cli-v0.2.5")
Expand Down
20 changes: 20 additions & 0 deletions R-package/R/000-wrappers.R
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,26 @@ set_name_external <- function(x, name) {
}


altint <- function() {
.Call(savvy_altint__impl)
}


altreal <- function() {
.Call(savvy_altreal__impl)
}


altlogical <- function() {
.Call(savvy_altlogical__impl)
}


altstring <- function() {
.Call(savvy_altstring__impl)
}


get_class_int <- function(x) {
.Call(savvy_get_class_int__impl, x)
}
Expand Down
30 changes: 27 additions & 3 deletions R-package/src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,28 @@ SEXP savvy_set_name_external__impl(SEXP x, SEXP name) {
return handle_result(res);
}

SEXP savvy_init_foo__impl(DllInfo* _dll_info) {
SEXP res = savvy_init_foo__ffi(_dll_info);
SEXP savvy_altint__impl(void) {
SEXP res = savvy_altint__ffi();
return handle_result(res);
}

SEXP savvy_altreal__impl(void) {
SEXP res = savvy_altreal__ffi();
return handle_result(res);
}

SEXP savvy_altlogical__impl(void) {
SEXP res = savvy_altlogical__ffi();
return handle_result(res);
}

SEXP savvy_altstring__impl(void) {
SEXP res = savvy_altstring__ffi();
return handle_result(res);
}

SEXP savvy_init_altrep_class__impl(DllInfo* dll_info) {
SEXP res = savvy_init_altrep_class__ffi(dll_info);
return handle_result(res);
}

Expand Down Expand Up @@ -508,6 +528,10 @@ static const R_CallMethodDef CallEntries[] = {
{"savvy_external_person_new__impl", (DL_FUNC) &savvy_external_person_new__impl, 0},
{"savvy_get_name_external__impl", (DL_FUNC) &savvy_get_name_external__impl, 1},
{"savvy_set_name_external__impl", (DL_FUNC) &savvy_set_name_external__impl, 2},
{"savvy_altint__impl", (DL_FUNC) &savvy_altint__impl, 0},
{"savvy_altreal__impl", (DL_FUNC) &savvy_altreal__impl, 0},
{"savvy_altlogical__impl", (DL_FUNC) &savvy_altlogical__impl, 0},
{"savvy_altstring__impl", (DL_FUNC) &savvy_altstring__impl, 0},
{"savvy_get_class_int__impl", (DL_FUNC) &savvy_get_class_int__impl, 1},
{"savvy_get_names_int__impl", (DL_FUNC) &savvy_get_names_int__impl, 1},
{"savvy_get_dim_int__impl", (DL_FUNC) &savvy_get_dim_int__impl, 1},
Expand Down Expand Up @@ -589,5 +613,5 @@ void R_init_savvyExamples(DllInfo *dll) {
R_useDynamicSymbols(dll, FALSE);

// Functions for initialzation, if any.
savvy_init_foo__impl(dll);
savvy_init_altrep_class__impl(dll);
}
3 changes: 2 additions & 1 deletion R-package/src/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ edition = "2021"
crate-type = ["staticlib", "lib"]

[dependencies]
savvy = { path = "../../../", features = ["complex"] }
savvy = { path = "../../../", features = ["complex", "altrep"] }
# for calling Rf_errorcall() to test error handling
savvy-ffi = { path = "../../../savvy-ffi" }

[profile.release]
Expand Down
6 changes: 5 additions & 1 deletion R-package/src/rust/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ SEXP savvy_list_with_names_and_values__ffi(void);
SEXP savvy_external_person_new__ffi(void);
SEXP savvy_get_name_external__ffi(SEXP x);
SEXP savvy_set_name_external__ffi(SEXP x, SEXP name);
SEXP savvy_init_foo__ffi(DllInfo* _dll_info);
SEXP savvy_altint__ffi(void);
SEXP savvy_altreal__ffi(void);
SEXP savvy_altlogical__ffi(void);
SEXP savvy_altstring__ffi(void);
SEXP savvy_init_altrep_class__ffi(DllInfo* dll_info);
SEXP savvy_get_class_int__ffi(SEXP x);
SEXP savvy_get_names_int__ffi(SEXP x);
SEXP savvy_get_dim_int__ffi(SEXP x);
Expand Down
140 changes: 140 additions & 0 deletions R-package/src/rust/src/altrep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use savvy::altrep::{
register_altinteger_class, register_altlogical_class, register_altreal_class,
register_altstring_class, AltInteger, AltLogical, AltReal, AltString,
};
use savvy::savvy;

// integer

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]
}
}

#[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))
}

// real

struct MyAltReal(Vec<f64>);
impl savvy::IntoExtPtrSexp for MyAltReal {}

impl MyAltReal {
fn new(x: Vec<f64>) -> Self {
Self(x)
}
}

impl AltReal for MyAltReal {
const CLASS_NAME: &'static str = "MyAltReal";
const PACKAGE_NAME: &'static str = "TestPackage";

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

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

#[savvy]
fn altreal() -> savvy::Result<savvy::Sexp> {
let v = MyAltReal::new(vec![1.0, 2.0, 3.0]);
let v_altrep = v.into_altrep()?;
Ok(savvy::Sexp(v_altrep))
}

// logical

struct MyAltLogical(Vec<bool>);
impl savvy::IntoExtPtrSexp for MyAltLogical {}

impl MyAltLogical {
fn new(x: Vec<bool>) -> Self {
Self(x)
}
}

impl AltLogical for MyAltLogical {
const CLASS_NAME: &'static str = "MyAltLogical";
const PACKAGE_NAME: &'static str = "TestPackage";

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

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

#[savvy]
fn altlogical() -> savvy::Result<savvy::Sexp> {
let v = MyAltLogical::new(vec![true, false, true]);
let v_altrep = v.into_altrep()?;
Ok(savvy::Sexp(v_altrep))
}

// string

struct MyAltString(Vec<String>);
impl savvy::IntoExtPtrSexp for MyAltString {}

impl MyAltString {
fn new(x: Vec<String>) -> Self {
Self(x)
}
}

impl AltString for MyAltString {
const CLASS_NAME: &'static str = "MyAltString";
const PACKAGE_NAME: &'static str = "TestPackage";

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

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

#[savvy]
fn altstring() -> savvy::Result<savvy::Sexp> {
let v = MyAltString::new(vec!["1".to_string(), "2".to_string(), "3".to_string()]);
let v_altrep = v.into_altrep()?;
Ok(savvy::Sexp(v_altrep))
}

// initialization

#[savvy]
fn init_altrep_class(dll_info: *mut savvy::ffi::DllInfo) -> savvy::Result<()> {
register_altinteger_class::<MyAltInt>(dll_info)?;
register_altreal_class::<MyAltReal>(dll_info)?;
register_altlogical_class::<MyAltLogical>(dll_info)?;
register_altstring_class::<MyAltString>(dll_info)?;
Ok(())
}
9 changes: 2 additions & 7 deletions R-package/src/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(unused_variables)]

mod altrep;
mod attributes;
mod complex;
mod consuming_type;
Expand All @@ -21,7 +22,7 @@ mod mod1;
// This should not be parsed
// mod mod2;

use savvy::{r_eprintln, r_print, savvy, OwnedListSexp};
use savvy::{r_print, savvy, OwnedListSexp};

use savvy::{
IntegerSexp, ListSexp, LogicalSexp, OwnedIntegerSexp, OwnedLogicalSexp, OwnedRealSexp,
Expand Down Expand Up @@ -422,9 +423,3 @@ mod tests {
Ok(())
}
}

#[savvy]
fn init_foo(_dll_info: *mut savvy::ffi::DllInfo) -> savvy::Result<()> {
r_eprintln!("Initialized!");
Ok(())
}
39 changes: 39 additions & 0 deletions R-package/tests/testthat/test-altrep.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
test_that("altinteger works", {
x <- altint()
expect_equal(x[1], 1L) # ELT method
expect_equal(length(x), 3L) # length method
expect_equal(as.character(x), c("1", "2", "3")) # coerce method
# duplicate method? dataptr method? I'm not sure
x[1] <- 2L
expect_equal(x, c(2L, 2L, 3L))
})

test_that("altreal works", {
x <- altreal()
expect_equal(x[1], 1) # ELT method
expect_equal(length(x), 3L) # length method
expect_equal(as.character(x), c("1", "2", "3")) # coerce method
# duplicate method? dataptr method? I'm not sure
x[1] <- 2
expect_equal(x, c(2, 2, 3))
})

test_that("altlogical works", {
x <- altlogical()
expect_equal(x[1], TRUE) # ELT method
expect_equal(length(x), 3L) # length method
expect_equal(as.character(x), c("TRUE", "FALSE", "TRUE")) # coerce method
# duplicate method? dataptr method? I'm not sure
x[1] <- FALSE
expect_equal(x, c(FALSE, FALSE, TRUE))
})

test_that("altstring works", {
x <- altstring()
expect_equal(x[1], "1") # ELT method
expect_equal(length(x), 3L) # length method
expect_equal(as.integer(x), c(1L, 2L, 3L)) # coerce method
# duplicate method? dataptr method? I'm not sure
x[1] <- "foo"
expect_equal(x, c("foo", "2", "3"))
})
1 change: 1 addition & 0 deletions savvy-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ num-complex = { version = "0.4.5", optional = true }
[features]
default = []
complex = ["num-complex"]
altrep = []
Loading

0 comments on commit 0cbaf98

Please sign in to comment.