Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not use SETLENGTH #214

Merged
merged 10 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/check-non-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:

REGEX=$(Rscript tmp.R)

NON_API=$(grep -R -E "${REGEX}" ./savvy-ffi/src/)
NON_API=$(grep -R -E "${REGEX}" ./savvy-ffi/src/ || true)

echo "Check result:"
echo
Expand All @@ -36,4 +36,6 @@ jobs:
if [ -n "${NON_API}" ]; then
echo 'Found what they call "non-API"!'
exit 1
else
echo "OK!"
fi
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
<!-- next-header -->
## [Unreleased] (ReleaseDate)

### Minor improvements

* Now savvy no longer uses `SETLENGTH`, which is a so-called "non-API" thing.

## [v0.6.0] (2024-04-20)

### Breaking changes
Expand Down
1 change: 0 additions & 1 deletion savvy-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ extern "C" {
// Allocation and attributes
extern "C" {
pub fn Rf_xlength(arg1: SEXP) -> R_xlen_t;
pub fn SETLENGTH(x: SEXP, v: R_xlen_t);
pub fn Rf_allocVector(arg1: SEXPTYPE, arg2: R_xlen_t) -> SEXP;
pub fn Rf_install(arg1: *const ::std::os::raw::c_char) -> SEXP;
pub fn Rf_getAttrib(arg1: SEXP, arg2: SEXP) -> SEXP;
Expand Down
31 changes: 19 additions & 12 deletions src/sexp/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use savvy_ffi::CPLXSXP;
use savvy_ffi::{COMPLEX, SEXP};

use super::{impl_common_sexp_ops, impl_common_sexp_ops_owned, utils::assert_len, Sexp};
use crate::protect;
use crate::protect::{self, local_protect};
use crate::NotAvailableValue; // for na()

/// An external SEXP of a complex vector.
Expand Down Expand Up @@ -198,27 +198,34 @@ impl OwnedComplexSexp {
// (e.g. `(0..10).filter(|x| x % 2 == 0)`), so it needs to be
// truncated to the actual length at last.

let mut out = unsafe { Self::new_without_init(upper)? };
let inner = crate::alloc_vector(CPLXSXP, upper as _)?;
local_protect(inner);
let raw = unsafe { COMPLEX(inner) };

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)?;
// and what we should not trust.
assert_len(upper, i)?;
unsafe { *(raw.add(i)) = v };

last_index = i;
}

let new_len = last_index + 1;
if new_len != upper {
unsafe {
savvy_ffi::SETLENGTH(out.inner, new_len as _);
}
out.len = new_len;
if new_len == upper {
// If the length is the same as expected, use it as it is.
Self::new_from_raw_sexp(inner, upper)
} else {
// If the length is shorter than expected, re-allocate a new
// SEXP and copy the values into it.
let mut out = unsafe { Self::new_without_init(new_len)? };
// `raw` is longer than new_len, but the elements over new_len are ignored
let raw_slice = unsafe { std::slice::from_raw_parts(raw, new_len) };
out.as_mut_slice().copy_from_slice(raw_slice);

Ok(out)
}

Ok(out)
}
(_, None) => {
// When the length is not known at all, collect() it first.
Expand Down
32 changes: 20 additions & 12 deletions src/sexp/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use savvy_ffi::{INTEGER, INTSXP, SEXP};

use super::utils::assert_len;
use super::{impl_common_sexp_ops, impl_common_sexp_ops_owned, Sexp};
use crate::protect;
use crate::protect::{self, local_protect};
use crate::NotAvailableValue; // for na()

/// An external SEXP of an integer vector.
Expand Down Expand Up @@ -284,27 +284,35 @@ impl OwnedIntegerSexp {
// (e.g. `(0..10).filter(|x| x % 2 == 0)`), so it needs to be
// truncated to the actual length at last.

let mut out = unsafe { Self::new_without_init(upper)? };
let inner = crate::alloc_vector(INTSXP, upper as _)?;
local_protect(inner);
let raw = unsafe { INTEGER(inner) };

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)?;
// and what we should not trust.
assert_len(upper, i)?;
unsafe { *(raw.add(i)) = v };

last_index = i;
}

let new_len = last_index + 1;
if new_len != upper {
unsafe {
savvy_ffi::SETLENGTH(out.inner, new_len as _);
}
out.len = new_len;
if new_len == upper {
// If the length is the same as expected, use it as it is.
Self::new_from_raw_sexp(inner, upper)
} else {
// If the length is shorter than expected, re-allocate a new
// SEXP and copy the values into it.
let out = unsafe { Self::new_without_init(new_len)? };
let dst = unsafe { std::slice::from_raw_parts_mut(out.raw, new_len) };
// `raw` is longer than new_len, but the elements over new_len are ignored
let src = unsafe { std::slice::from_raw_parts(raw, new_len) };
dst.copy_from_slice(src);

Ok(out)
}

Ok(out)
}
(_, None) => {
// When the length is not known at all, collect() it first.
Expand Down
34 changes: 21 additions & 13 deletions src/sexp/logical.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use savvy_ffi::{R_NaInt, LGLSXP, LOGICAL, SET_LOGICAL_ELT, SEXP};

use super::{impl_common_sexp_ops, impl_common_sexp_ops_owned, Sexp};
use crate::protect;
use super::{impl_common_sexp_ops, impl_common_sexp_ops_owned, utils::assert_len, Sexp};
use crate::protect::{self, local_protect};

/// An external SEXP of a logical vector.
pub struct LogicalSexp(pub SEXP);
Expand Down Expand Up @@ -258,27 +258,35 @@ impl OwnedLogicalSexp {
// (e.g. `(0..10).filter(|x| x % 2 == 0)`), so it needs to be
// truncated to the actual length at last.

let mut out = unsafe { Self::new_without_init(upper)? };
let inner = crate::alloc_vector(LGLSXP, upper as _)?;
local_protect(inner);
let raw = unsafe { LOGICAL(inner) };

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)?;
// and what we should not trust.
assert_len(upper, i)?;
unsafe { *(raw.add(i)) = v as _ };

last_index = i;
}

let new_len = last_index + 1;
if new_len != upper {
unsafe {
savvy_ffi::SETLENGTH(out.inner, new_len as _);
}
out.len = new_len;
if new_len == upper {
// If the length is the same as expected, use it as it is.
Self::new_from_raw_sexp(inner, upper)
} else {
// If the length is shorter than expected, re-allocate a new
// SEXP and copy the values into it.
let out = unsafe { Self::new_without_init(new_len)? };
let dst = unsafe { std::slice::from_raw_parts_mut(out.raw, new_len) };
// `raw` is longer than new_len, but the elements over new_len are ignored
let src = unsafe { std::slice::from_raw_parts(raw, new_len) };
dst.copy_from_slice(src);

Ok(out)
}

Ok(out)
}
(_, None) => {
// When the length is not known at all, collect() it first.
Expand Down
32 changes: 20 additions & 12 deletions src/sexp/real.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use savvy_ffi::{REAL, REALSXP, SEXP};

use super::utils::assert_len;
use super::{impl_common_sexp_ops, impl_common_sexp_ops_owned, Sexp};
use crate::protect;
use crate::protect::{self, local_protect};
use crate::NotAvailableValue; // for na()

/// An external SEXP of a real vector.
Expand Down Expand Up @@ -287,27 +287,35 @@ impl OwnedRealSexp {
// (e.g. `(0..10).filter(|x| x % 2 == 0)`), so it needs to be
// truncated to the actual length at last.

let mut out = unsafe { Self::new_without_init(upper)? };
let inner = crate::alloc_vector(REALSXP, upper as _)?;
local_protect(inner);
let raw = unsafe { REAL(inner) };

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)?;
// and what we should not trust.
assert_len(upper, i)?;
unsafe { *(raw.add(i)) = v };

last_index = i;
}

let new_len = last_index + 1;
if new_len != upper {
unsafe {
savvy_ffi::SETLENGTH(out.inner, new_len as _);
}
out.len = new_len;
if new_len == upper {
// If the length is the same as expected, use it as it is.
Self::new_from_raw_sexp(inner, upper)
} else {
// If the length is shorter than expected, re-allocate a new
// SEXP and copy the values into it.
let out = unsafe { Self::new_without_init(new_len)? };
let dst = unsafe { std::slice::from_raw_parts_mut(out.raw, new_len) };
// `raw` is longer than new_len, but the elements over new_len are ignored
let src = unsafe { std::slice::from_raw_parts(raw, new_len) };
dst.copy_from_slice(src);

Ok(out)
}

Ok(out)
}
(_, None) => {
// When the length is not known at all, collect() it first.
Expand Down
26 changes: 15 additions & 11 deletions src/sexp/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,28 +187,32 @@ impl OwnedStringSexp {
// (e.g. `(0..10).filter(|x| x % 2 == 0)`), so it needs to be
// truncated to the actual length at last.

// string doesn't have new_without_init() method
let mut out = Self::new(upper)?;
let inner = crate::alloc_vector(STRSXP, upper as _)?;
local_protect(inner);

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())?;
// and what we should not trust.
assert_len(upper, i)?;
unsafe { SET_STRING_ELT(inner, i as _, str_to_charsxp(v.as_ref())?) };

last_index = i;
}

let new_len = last_index + 1;
if new_len != upper {
unsafe {
savvy_ffi::SETLENGTH(out.inner, new_len as _);
if new_len == upper {
// If the length is the same as expected, use it as it is.
Self::new_from_raw_sexp(inner, upper)
} else {
// If the length is shorter than expected, re-allocate a new
// SEXP and copy the values into it.
let mut out = Self::new(new_len)?;
for i in 0..new_len {
unsafe { out.set_elt_unchecked(i, STRING_ELT(inner, i as _)) };
}
out.len = new_len;
Ok(out)
}

Ok(out)
}
(_, None) => {
// When the length is not known at all, collect() it first.
Expand Down
1 change: 0 additions & 1 deletion xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ fn show() -> Result<(), DynError> {
.allowlist_function("R_IsNA")
// Allocation and attributes
.allowlist_function("Rf_xlength")
.allowlist_function("SETLENGTH")
.allowlist_function("Rf_allocVector")
.allowlist_function("Rf_install")
.allowlist_function("Rf_getAttrib")
Expand Down