Skip to content

Commit

Permalink
ALTRAW (#276)
Browse files Browse the repository at this point in the history
  • Loading branch information
yutannihilation committed Jul 2, 2024
1 parent 6f91d10 commit 0ea3542
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 7 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
<!-- next-header -->
## [Unreleased] (ReleaseDate)

### New features

* Add support for raw, including ALTRAW.
Please be aware that, while this support was added for consistency, I bet it's
really rare that a raw vector is actually needed; if you want to deal with a
binary data on Rust's side, your primary option should be to store it in an
external pointer (of a struct you define) rather than an R's raw vector.

### Minor Improvements

* Wrapper environment of a Rust struct or enum now cannot be modified by users.
Expand Down
15 changes: 15 additions & 0 deletions R-package/R/000-wrappers.R
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,21 @@ NULL
}


`altraw` <- function() {
.Call(savvy_altraw__impl)
}


`print_altraw` <- function(`x`) {
invisible(.Call(savvy_print_altraw__impl, `x`))
}


`tweak_altraw` <- function(`x`) {
invisible(.Call(savvy_tweak_altraw__impl, `x`))
}


`altstring` <- function() {
.Call(savvy_altstring__impl)
}
Expand Down
18 changes: 18 additions & 0 deletions R-package/src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,21 @@ SEXP savvy_tweak_altlogical__impl(SEXP x) {
return handle_result(res);
}

SEXP savvy_altraw__impl(void) {
SEXP res = savvy_altraw__ffi();
return handle_result(res);
}

SEXP savvy_print_altraw__impl(SEXP x) {
SEXP res = savvy_print_altraw__ffi(x);
return handle_result(res);
}

SEXP savvy_tweak_altraw__impl(SEXP x) {
SEXP res = savvy_tweak_altraw__ffi(x);
return handle_result(res);
}

SEXP savvy_altstring__impl(void) {
SEXP res = savvy_altstring__ffi();
return handle_result(res);
Expand Down Expand Up @@ -714,6 +729,9 @@ static const R_CallMethodDef CallEntries[] = {
{"savvy_altlogical__impl", (DL_FUNC) &savvy_altlogical__impl, 0},
{"savvy_print_altlogical__impl", (DL_FUNC) &savvy_print_altlogical__impl, 1},
{"savvy_tweak_altlogical__impl", (DL_FUNC) &savvy_tweak_altlogical__impl, 1},
{"savvy_altraw__impl", (DL_FUNC) &savvy_altraw__impl, 0},
{"savvy_print_altraw__impl", (DL_FUNC) &savvy_print_altraw__impl, 1},
{"savvy_tweak_altraw__impl", (DL_FUNC) &savvy_tweak_altraw__impl, 1},
{"savvy_altstring__impl", (DL_FUNC) &savvy_altstring__impl, 0},
{"savvy_print_altstring__impl", (DL_FUNC) &savvy_print_altstring__impl, 1},
{"savvy_tweak_altstring__impl", (DL_FUNC) &savvy_tweak_altstring__impl, 1},
Expand Down
3 changes: 3 additions & 0 deletions R-package/src/rust/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ SEXP savvy_tweak_altreal__ffi(SEXP x);
SEXP savvy_altlogical__ffi(void);
SEXP savvy_print_altlogical__ffi(SEXP x);
SEXP savvy_tweak_altlogical__ffi(SEXP x);
SEXP savvy_altraw__ffi(void);
SEXP savvy_print_altraw__ffi(SEXP x);
SEXP savvy_tweak_altraw__ffi(SEXP x);
SEXP savvy_altstring__ffi(void);
SEXP savvy_print_altstring__ffi(SEXP x);
SEXP savvy_tweak_altstring__ffi(SEXP x);
Expand Down
62 changes: 59 additions & 3 deletions R-package/src/rust/src/altrep.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use savvy::altrep::{
get_altrep_body_ref_unchecked, register_altinteger_class, register_altlist_class,
register_altlogical_class, register_altreal_class, register_altstring_class, AltInteger,
AltList, AltLogical, AltReal, AltString,
register_altlogical_class, register_altraw_class, register_altreal_class,
register_altstring_class, AltInteger, AltList, AltLogical, AltRaw, AltReal, AltString,
};
use savvy::{
r_println, savvy, savvy_init, IntegerSexp, ListSexp, LogicalSexp, NullSexp, RealSexp,
r_println, savvy, savvy_init, IntegerSexp, ListSexp, LogicalSexp, NullSexp, RawSexp, RealSexp,
StringSexp,
};

Expand Down Expand Up @@ -173,6 +173,61 @@ fn tweak_altlogical(mut x: LogicalSexp) -> savvy::Result<()> {
Err("Not a known ALTREP".into())
}

// raw

#[derive(Debug, Clone)]
struct MyAltRaw(Vec<u8>);

impl MyAltRaw {
fn new(x: Vec<u8>) -> Self {
Self(x)
}
}

impl savvy::IntoExtPtrSexp for MyAltRaw {}

impl AltRaw for MyAltRaw {
const CLASS_NAME: &'static str = "MyAltRaw";
const PACKAGE_NAME: &'static str = "TestPackage";

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

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

#[savvy]
fn altraw() -> savvy::Result<savvy::Sexp> {
let v = MyAltRaw::new(vec![1u8, 2, 3]);
v.into_altrep()
}

#[savvy]
fn print_altraw(x: RawSexp) -> savvy::Result<()> {
if let Ok(x) = MyAltRaw::try_from_altrep_ref(&x) {
r_println!("{x:?}");
return Ok(());
};

Err("Not a known ALTREP".into())
}

#[savvy]
fn tweak_altraw(mut x: RawSexp) -> savvy::Result<()> {
if let Ok(x) = MyAltRaw::try_from_altrep_mut(&mut x, true) {
for i in x.0.iter_mut() {
*i += 1;
}
x.0.push(2);
return Ok(());
};

Err("Not a known ALTREP".into())
}

// string

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -311,6 +366,7 @@ 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_altraw_class::<MyAltRaw>(dll_info)?;
register_altstring_class::<MyAltString>(dll_info)?;
register_altlist_class::<MyAltList>(dll_info)?;
Ok(())
Expand Down
22 changes: 22 additions & 0 deletions R-package/tests/testthat/test-altrep.R
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,28 @@ test_that("altlogical works", {
expect_equal(x, c(NA, TRUE, FALSE, FALSE))
})

test_that("altraw works", {
x <- altraw()

expect_equal(x[1], as.raw(1L)) # ELT method
expect_equal(length(x), 3L) # length method
expect_equal(as.character(x), c("01", "02", "03")) # coerce method
expect_equal(x, as.raw(1:3))

# after the first materialization, the values reflect to R's side if invalidate_cache = TRUE
tweak_altraw(x)
expect_output(print_altraw(x), "MyAltRaw([2, 3, 4, 2])", fixed = TRUE)

expect_equal(x[1], as.raw(2L)) # ELT method
expect_equal(length(x), 4L) # length method
expect_equal(as.character(x), c("02", "03", "04", "02")) # coerce method
expect_equal(x, as.raw(c(2L, 3L, 4L, 2L)))

# duplicate method? dataptr method? I'm not sure
x[1] <- as.raw(100L)
expect_equal(x, as.raw(c(100L, 3L, 4L, 2L)))
})

test_that("altstring works", {
x <- altstring()

Expand Down
26 changes: 25 additions & 1 deletion savvy-ffi/src/altrep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ extern "C" {
);
}

// Real
// real

extern "C" {
pub fn R_make_altreal_class(
Expand Down Expand Up @@ -212,6 +212,30 @@ extern "C" {
);
}

// raw
extern "C" {
pub fn R_make_altraw_class(
cname: *const c_char,
pname: *const c_char,
info: *mut crate::DllInfo,
) -> R_altrep_class_t;
pub fn R_set_altraw_Elt_method(
cls: R_altrep_class_t,
fun: Option<unsafe extern "C" fn(arg1: SEXP, arg2: R_xlen_t) -> u8>,
);
pub fn R_set_altraw_Get_region_method(
cls: R_altrep_class_t,
fun: Option<
unsafe extern "C" fn(
arg1: SEXP,
arg2: R_xlen_t,
arg3: R_xlen_t,
arg4: *mut u8,
) -> R_xlen_t,
>,
);
}

// string

extern "C" {
Expand Down
Loading

0 comments on commit 0ea3542

Please sign in to comment.