Skip to content

Commit

Permalink
Some followup to #145 (#146)
Browse files Browse the repository at this point in the history
* Set #![warn(unsafe_op_in_unsafe_fn)]

* Fix unsafe_op_in_unsafe_fn warnings

* Add set_elt_unchecked() for completeness

* Use isize for the interface

* Add comments why we shouldn't use set_elt_unchecked() here
  • Loading branch information
yutannihilation authored Mar 31, 2024
1 parent 0b45c86 commit 0dd1d3f
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 41 deletions.
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// TODO: Remove this when edition is set to 2024
#![warn(unsafe_op_in_unsafe_fn)]

//! # Savvy - A Simple R Interface
//!
//! **savvy** is a simple R extension interface using Rust, like the [extendr] framework.
Expand Down
17 changes: 11 additions & 6 deletions src/sexp/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,21 @@ impl OwnedComplexSexp {
pub fn set_elt(&mut self, i: usize, v: Complex64) -> crate::error::Result<()> {
super::utils::assert_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = v;
}
unsafe { self.set_elt_unchecked(i, v) };

Ok(())
}

#[inline]
unsafe fn set_elt_unchecked(&mut self, i: usize, v: Complex64) {
unsafe { *(self.raw.add(i)) = v };
}

/// Set the `i`-th element to NA.
pub fn set_na(&mut self, i: usize) -> crate::error::Result<()> {
super::utils::assert_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = Complex64::na();
}
unsafe { self.set_elt_unchecked(i, Complex64::na()) };

Ok(())
}
Expand Down Expand Up @@ -144,7 +145,11 @@ impl OwnedComplexSexp {

let mut last_index = 0;
for (i, v) in iter.enumerate() {
// The upper bound of size_hint() is just for optimization
// and what we should not trust. So, we should't use
// `set_elt_unchecked()` here.
out.set_elt(i, v)?;

last_index = i;
}

Expand Down
17 changes: 11 additions & 6 deletions src/sexp/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,21 @@ impl OwnedIntegerSexp {
pub fn set_elt(&mut self, i: usize, v: i32) -> crate::error::Result<()> {
super::utils::assert_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = v;
}
unsafe { self.set_elt_unchecked(i, v) };

Ok(())
}

#[inline]
unsafe fn set_elt_unchecked(&mut self, i: usize, v: i32) {
unsafe { *(self.raw.add(i)) = v };
}

/// Set the `i`-th element to NA.
pub fn set_na(&mut self, i: usize) -> crate::error::Result<()> {
super::utils::assert_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = i32::na();
}
unsafe { self.set_elt_unchecked(i, i32::na()) };

Ok(())
}
Expand Down Expand Up @@ -173,7 +174,11 @@ impl OwnedIntegerSexp {

let mut last_index = 0;
for (i, v) in iter.enumerate() {
// The upper bound of size_hint() is just for optimization
// and what we should not trust. So, we should't use
// `set_elt_unchecked()` here.
out.set_elt(i, v)?;

last_index = i;
}

Expand Down
15 changes: 10 additions & 5 deletions src/sexp/logical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,23 @@ impl OwnedLogicalSexp {
pub fn set_elt(&mut self, i: usize, v: bool) -> crate::error::Result<()> {
super::utils::assert_len(self.len, i)?;

unsafe { self.set_elt_unchecked(i as _, v as _) };
unsafe { self.set_elt_unchecked(i, v as _) };

Ok(())
}

// Set the value of the `i`-th element.
// Safety: the user has to assure bounds are checked.
pub(crate) unsafe fn set_elt_unchecked(&mut self, i: isize, v: i32) {
SET_LOGICAL_ELT(self.inner, i, v);
#[inline]
unsafe fn set_elt_unchecked(&mut self, i: usize, v: i32) {
unsafe { SET_LOGICAL_ELT(self.inner, i as _, v) };
}

/// Set the `i`-th element to NA.
pub fn set_na(&mut self, i: usize) -> crate::error::Result<()> {
super::utils::assert_len(self.len, i)?;

unsafe { self.set_elt_unchecked(i as _, R_NaInt) };
unsafe { self.set_elt_unchecked(i, R_NaInt) };

Ok(())
}
Expand Down Expand Up @@ -169,7 +170,11 @@ impl OwnedLogicalSexp {

let mut last_index = 0;
for (i, v) in iter.enumerate() {
// The upper bound of size_hint() is just for optimization
// and what we should not trust. So, we should't use
// `set_elt_unchecked()` here.
out.set_elt(i, v)?;

last_index = i;
}

Expand Down Expand Up @@ -199,7 +204,7 @@ impl OwnedLogicalSexp {
let mut out = unsafe { Self::new_without_init(x_slice.len())? };
for (i, v) in x_slice.iter().enumerate() {
// Safety: slice and OwnedLogicalSexp have the same length.
unsafe { out.set_elt_unchecked(i as _, *v as _) };
unsafe { out.set_elt_unchecked(i, *v as _) };
}
Ok(out)
}
Expand Down
17 changes: 11 additions & 6 deletions src/sexp/real.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,21 @@ impl OwnedRealSexp {
pub fn set_elt(&mut self, i: usize, v: f64) -> crate::error::Result<()> {
super::utils::assert_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = v;
}
unsafe { self.set_elt_unchecked(i, v) };

Ok(())
}

#[inline]
unsafe fn set_elt_unchecked(&mut self, i: usize, v: f64) {
unsafe { *(self.raw.add(i)) = v };
}

/// Set the `i`-th element to NA.
pub fn set_na(&mut self, i: usize) -> crate::error::Result<()> {
super::utils::assert_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = f64::na();
}
unsafe { self.set_elt_unchecked(i, f64::na()) };

Ok(())
}
Expand Down Expand Up @@ -169,7 +170,11 @@ impl OwnedRealSexp {

let mut last_index = 0;
for (i, v) in iter.enumerate() {
// The upper bound of size_hint() is just for optimization
// and what we should not trust. So, we should't use
// `set_elt_unchecked()` here.
out.set_elt(i, v)?;

last_index = i;
}

Expand Down
43 changes: 25 additions & 18 deletions src/sexp/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,16 @@ impl OwnedStringSexp {
self.len, i
)));
}
unsafe { self.set_elt_unchecked(i as _, str_to_charsxp(v)?) };
unsafe { self.set_elt_unchecked(i, str_to_charsxp(v)?) };

Ok(())
}

// Set the value of the `i`-th element.
// Safety: the user has to assure bounds are checked.
pub(crate) unsafe fn set_elt_unchecked(&mut self, i: isize, v: SEXP) {
SET_STRING_ELT(self.inner, i, v);
#[inline]
unsafe fn set_elt_unchecked(&mut self, i: usize, v: SEXP) {
unsafe { SET_STRING_ELT(self.inner, i as _, v) };
}

/// Set the `i`-th element to NA.
Expand All @@ -83,7 +84,7 @@ impl OwnedStringSexp {
)));
}

unsafe { self.set_elt_unchecked(i as _, R_NaString) };
unsafe { self.set_elt_unchecked(i, R_NaString) };

Ok(())
}
Expand Down Expand Up @@ -131,7 +132,11 @@ impl OwnedStringSexp {

let mut last_index = 0;
for (i, v) in iter.enumerate() {
// The upper bound of size_hint() is just for optimization
// and what we should not trust. So, we should't use
// `set_elt_unchecked()` here.
out.set_elt(i, v.as_ref())?;

last_index = i;
}

Expand Down Expand Up @@ -162,7 +167,7 @@ impl OwnedStringSexp {
let mut out = Self::new(x_slice.len())?;
for (i, v) in x_slice.iter().enumerate() {
// Safety: slice and OwnedStringSexp have the same length.
unsafe { out.set_elt_unchecked(i as _, str_to_charsxp(v.as_ref())?) };
unsafe { out.set_elt_unchecked(i, str_to_charsxp(v.as_ref())?) };
}
Ok(out)
}
Expand All @@ -183,19 +188,21 @@ impl OwnedStringSexp {
}

unsafe fn str_to_charsxp(v: &str) -> crate::error::Result<SEXP> {
// We might be able to put `R_NaString` directly without using
// <&str>::na(), but probably this is an inevitable cost of
// providing <&str>::na().
if v.is_na() {
Ok(savvy_ffi::R_NaString)
} else {
crate::unwind_protect(|| {
Rf_mkCharLenCE(
v.as_ptr() as *const c_char,
v.len() as i32,
cetype_t_CE_UTF8,
)
})
unsafe {
// We might be able to put `R_NaString` directly without using
// <&str>::na(), but probably this is an inevitable cost of
// providing <&str>::na().
if v.is_na() {
Ok(savvy_ffi::R_NaString)
} else {
crate::unwind_protect(|| {
Rf_mkCharLenCE(
v.as_ptr() as *const c_char,
v.len() as i32,
cetype_t_CE_UTF8,
)
})
}
}
}

Expand Down

0 comments on commit 0dd1d3f

Please sign in to comment.