diff --git a/savvy-ffi/src/lib.rs b/savvy-ffi/src/lib.rs index ce828964..fddc286c 100644 --- a/savvy-ffi/src/lib.rs +++ b/savvy-ffi/src/lib.rs @@ -66,6 +66,7 @@ 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; diff --git a/src/sexp/complex.rs b/src/sexp/complex.rs index a7ba9866..42fa9619 100644 --- a/src/sexp/complex.rs +++ b/src/sexp/complex.rs @@ -121,6 +121,35 @@ impl OwnedComplexSexp { raw, }) } + + pub fn try_from_iter(iter: I) -> crate::error::Result + where + I: Iterator, + { + let iter = iter.into_iter(); + match iter.size_hint() { + (_, Some(upper)) => { + let mut out = unsafe { Self::new_without_init(upper)? }; + let mut actual_len = 0; + for (i, v) in iter.enumerate() { + out.set_elt(i, v)?; + actual_len = i; + } + + if actual_len != upper { + unsafe { + savvy_ffi::SETLENGTH(out.inner, actual_len as _); + } + } + + Ok(out) + } + (_, None) => { + let v: Vec = iter.collect(); + v.try_into() + } + } + } } impl Drop for OwnedComplexSexp { diff --git a/src/sexp/integer.rs b/src/sexp/integer.rs index 46c114a4..9ad831ff 100644 --- a/src/sexp/integer.rs +++ b/src/sexp/integer.rs @@ -150,6 +150,35 @@ impl OwnedIntegerSexp { raw, }) } + + pub fn try_from_iter(iter: I) -> crate::error::Result + where + I: Iterator, + { + let iter = iter.into_iter(); + match iter.size_hint() { + (_, Some(upper)) => { + let mut out = unsafe { Self::new_without_init(upper)? }; + let mut actual_len = 0; + for (i, v) in iter.enumerate() { + out.set_elt(i, v)?; + actual_len = i; + } + + if actual_len != upper { + unsafe { + savvy_ffi::SETLENGTH(out.inner, actual_len as _); + } + } + + Ok(out) + } + (_, None) => { + let v: Vec = iter.collect(); + v.try_into() + } + } + } } impl Drop for OwnedIntegerSexp { diff --git a/src/sexp/logical.rs b/src/sexp/logical.rs index 89d16163..e7c99218 100644 --- a/src/sexp/logical.rs +++ b/src/sexp/logical.rs @@ -145,6 +145,35 @@ impl OwnedLogicalSexp { raw, }) } + + pub fn try_from_iter(iter: I) -> crate::error::Result + where + I: Iterator, + { + let iter = iter.into_iter(); + match iter.size_hint() { + (_, Some(upper)) => { + let mut out = unsafe { Self::new_without_init(upper)? }; + let mut actual_len = 0; + for (i, v) in iter.enumerate() { + out.set_elt(i, v)?; + actual_len = i; + } + + if actual_len != upper { + unsafe { + savvy_ffi::SETLENGTH(out.inner, actual_len as _); + } + } + + Ok(out) + } + (_, None) => { + let v: Vec = iter.collect(); + v.try_into() + } + } + } } impl Drop for OwnedLogicalSexp { diff --git a/src/sexp/real.rs b/src/sexp/real.rs index 3b43875e..105529ff 100644 --- a/src/sexp/real.rs +++ b/src/sexp/real.rs @@ -146,6 +146,35 @@ impl OwnedRealSexp { raw, }) } + + pub fn try_from_iter(iter: I) -> crate::error::Result + where + I: Iterator, + { + let iter = iter.into_iter(); + match iter.size_hint() { + (_, Some(upper)) => { + let mut out = unsafe { Self::new_without_init(upper)? }; + let mut actual_len = 0; + for (i, v) in iter.enumerate() { + out.set_elt(i, v)?; + actual_len = i; + } + + if actual_len != upper { + unsafe { + savvy_ffi::SETLENGTH(out.inner, actual_len as _); + } + } + + Ok(out) + } + (_, None) => { + let v: Vec = iter.collect(); + v.try_into() + } + } + } } impl Drop for OwnedRealSexp { diff --git a/src/sexp/string.rs b/src/sexp/string.rs index 7bfa5d9b..1d20b589 100644 --- a/src/sexp/string.rs +++ b/src/sexp/string.rs @@ -105,6 +105,36 @@ impl OwnedStringSexp { Ok(Self { inner, token, len }) } + + pub fn try_from_iter(iter: I) -> crate::error::Result + where + I: Iterator, + U: AsRef, + { + let iter = iter.into_iter(); + match iter.size_hint() { + (_, Some(upper)) => { + let mut out = Self::new(upper)?; + let mut actual_len = 0; + for (i, v) in iter.enumerate() { + out.set_elt(i, v.as_ref())?; + actual_len = i; + } + + if actual_len != upper { + unsafe { + savvy_ffi::SETLENGTH(out.inner, actual_len as _); + } + } + + Ok(out) + } + (_, None) => { + let v: Vec = iter.collect(); + v.try_into() + } + } + } } unsafe fn str_to_charsxp(v: &str) -> crate::error::Result { diff --git a/xtask/src/main.rs b/xtask/src/main.rs index a6fd2a3b..c247ac52 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -84,6 +84,7 @@ 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")