use std::collections::HashMap;
use std::iter::IntoIterator;
use std::ops::{Range, RangeInclusive};
use std::os::raw;
use extendr_ffi::{
dataptr, R_IsNA, R_NilValue, R_compute_identical, R_tryEval, Rboolean, Rcomplex, Rf_getAttrib,
Rf_setAttrib, Rf_xlength, COMPLEX, INTEGER, LOGICAL, PRINTNAME, RAW, REAL, SEXPTYPE,
SEXPTYPE::*, STRING_ELT, STRING_PTR_RO, TYPEOF, XLENGTH,
};
use crate::scalar::{Rbool, Rfloat, Rint};
use crate::*;
pub use into_robj::*;
pub use iter::*;
pub use operators::Operators;
use prelude::{c64, Rcplx};
pub use rinternals::Rinternals;
mod debug;
mod into_robj;
mod operators;
mod rinternals;
mod try_from_robj;
#[cfg(test)]
mod tests;
#[repr(transparent)]
pub struct Robj {
inner: SEXP,
}
impl Clone for Robj {
fn clone(&self) -> Self {
unsafe { Robj::from_sexp(self.get()) }
}
}
impl Default for Robj {
fn default() -> Self {
Robj::from(())
}
}
pub trait GetSexp {
unsafe fn get(&self) -> SEXP;
unsafe fn get_mut(&mut self) -> SEXP;
fn as_robj(&self) -> &Robj;
fn as_robj_mut(&mut self) -> &mut Robj;
}
impl GetSexp for Robj {
unsafe fn get(&self) -> SEXP {
self.inner
}
unsafe fn get_mut(&mut self) -> SEXP {
self.inner
}
fn as_robj(&self) -> &Robj {
unsafe { std::mem::transmute(&self.inner) }
}
fn as_robj_mut(&mut self) -> &mut Robj {
unsafe { std::mem::transmute(&mut self.inner) }
}
}
pub trait Slices: GetSexp {
unsafe fn as_typed_slice_raw<T>(&self) -> &[T] {
let len = XLENGTH(self.get()) as usize;
let data = dataptr(self.get()) as *const T;
std::slice::from_raw_parts(data, len)
}
unsafe fn as_typed_slice_raw_mut<T>(&mut self) -> &mut [T] {
let len = XLENGTH(self.get()) as usize;
let data = dataptr(self.get_mut()) as *mut T;
std::slice::from_raw_parts_mut(data, len)
}
}
impl Slices for Robj {}
pub trait Length: GetSexp {
fn len(&self) -> usize {
unsafe { Rf_xlength(self.get()) as usize }
}
fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Length for Robj {}
impl Robj {
pub unsafe fn from_sexp(sexp: SEXP) -> Self {
single_threaded(|| {
unsafe { ownership::protect(sexp) };
Robj { inner: sexp }
})
}
}
pub trait Types: GetSexp {
#[doc(hidden)]
fn sexptype(&self) -> SEXPTYPE {
unsafe { TYPEOF(self.get()) }
}
fn rtype(&self) -> Rtype {
use SEXPTYPE::*;
match self.sexptype() {
NILSXP => Rtype::Null,
SYMSXP => Rtype::Symbol,
LISTSXP => Rtype::Pairlist,
CLOSXP => Rtype::Function,
ENVSXP => Rtype::Environment,
PROMSXP => Rtype::Promise,
LANGSXP => Rtype::Language,
SPECIALSXP => Rtype::Special,
BUILTINSXP => Rtype::Builtin,
CHARSXP => Rtype::Rstr,
LGLSXP => Rtype::Logicals,
INTSXP => Rtype::Integers,
REALSXP => Rtype::Doubles,
CPLXSXP => Rtype::Complexes,
STRSXP => Rtype::Strings,
DOTSXP => Rtype::Dot,
ANYSXP => Rtype::Any,
VECSXP => Rtype::List,
EXPRSXP => Rtype::Expressions,
BCODESXP => Rtype::Bytecode,
EXTPTRSXP => Rtype::ExternalPtr,
WEAKREFSXP => Rtype::WeakRef,
RAWSXP => Rtype::Raw,
#[cfg(not(use_objsxp))]
S4SXP => Rtype::S4,
#[cfg(use_objsxp)]
OBJSXP => Rtype::S4,
_ => Rtype::Unknown,
}
}
fn as_any(&self) -> Rany<'_> {
use SEXPTYPE::*;
unsafe {
match self.sexptype() {
NILSXP => Rany::Null(self.as_robj()),
SYMSXP => Rany::Symbol(std::mem::transmute::<&Robj, &Symbol>(self.as_robj())),
LISTSXP => Rany::Pairlist(std::mem::transmute::<&Robj, &Pairlist>(self.as_robj())),
CLOSXP => Rany::Function(std::mem::transmute::<&Robj, &Function>(self.as_robj())),
ENVSXP => {
Rany::Environment(std::mem::transmute::<&Robj, &Environment>(self.as_robj()))
}
PROMSXP => Rany::Promise(std::mem::transmute::<&Robj, &Promise>(self.as_robj())),
LANGSXP => Rany::Language(std::mem::transmute::<&Robj, &Language>(self.as_robj())),
SPECIALSXP => {
Rany::Special(std::mem::transmute::<&Robj, &Primitive>(self.as_robj()))
}
BUILTINSXP => {
Rany::Builtin(std::mem::transmute::<&Robj, &Primitive>(self.as_robj()))
}
CHARSXP => Rany::Rstr(std::mem::transmute::<&Robj, &Rstr>(self.as_robj())),
LGLSXP => Rany::Logicals(std::mem::transmute::<&Robj, &Logicals>(self.as_robj())),
INTSXP => Rany::Integers(std::mem::transmute::<&Robj, &Integers>(self.as_robj())),
REALSXP => Rany::Doubles(std::mem::transmute::<&Robj, &Doubles>(self.as_robj())),
CPLXSXP => {
Rany::Complexes(std::mem::transmute::<&Robj, &Complexes>(self.as_robj()))
}
STRSXP => Rany::Strings(std::mem::transmute::<&Robj, &Strings>(self.as_robj())),
DOTSXP => Rany::Dot(std::mem::transmute::<&Robj, &Robj>(self.as_robj())),
ANYSXP => Rany::Any(std::mem::transmute::<&Robj, &Robj>(self.as_robj())),
VECSXP => Rany::List(std::mem::transmute::<&Robj, &List>(self.as_robj())),
EXPRSXP => {
Rany::Expressions(std::mem::transmute::<&Robj, &Expressions>(self.as_robj()))
}
BCODESXP => Rany::Bytecode(std::mem::transmute::<&Robj, &Robj>(self.as_robj())),
EXTPTRSXP => Rany::ExternalPtr(std::mem::transmute::<&Robj, &Robj>(self.as_robj())),
WEAKREFSXP => Rany::WeakRef(std::mem::transmute::<&Robj, &Robj>(self.as_robj())),
RAWSXP => Rany::Raw(std::mem::transmute::<&Robj, &Raw>(self.as_robj())),
#[cfg(not(use_objsxp))]
S4SXP => Rany::S4(std::mem::transmute(self.as_robj())),
#[cfg(use_objsxp)]
OBJSXP => Rany::S4(std::mem::transmute::<&Robj, &S4>(self.as_robj())),
_ => Rany::Unknown(std::mem::transmute::<&Robj, &Robj>(self.as_robj())),
}
}
}
}
impl Types for Robj {}
impl Robj {
pub fn is_na(&self) -> bool {
if self.len() != 1 {
false
} else {
unsafe {
let sexp = self.get();
use SEXPTYPE::*;
match self.sexptype() {
STRSXP => STRING_ELT(sexp, 0) == extendr_ffi::R_NaString,
INTSXP => *(INTEGER(sexp)) == extendr_ffi::R_NaInt,
LGLSXP => *(LOGICAL(sexp)) == extendr_ffi::R_NaInt,
REALSXP => R_IsNA(*(REAL(sexp))) != 0,
CPLXSXP => R_IsNA((*COMPLEX(sexp)).r) != 0,
CHARSXP => sexp == extendr_ffi::R_NaString,
_ => false,
}
}
}
}
pub fn as_integer_slice<'a>(&self) -> Option<&'a [i32]> {
self.as_typed_slice()
}
pub fn as_integers(&self) -> Option<Integers> {
self.clone().try_into().ok()
}
pub fn as_integer_vector(&self) -> Option<Vec<i32>> {
self.as_integer_slice().map(|value| value.to_vec())
}
pub fn as_logical_slice(&self) -> Option<&[Rbool]> {
self.as_typed_slice()
}
pub fn as_logical_vector(&self) -> Option<Vec<Rbool>> {
self.as_logical_slice().map(|value| value.to_vec())
}
pub fn as_logical_iter(&self) -> Option<impl Iterator<Item = &Rbool>> {
self.as_logical_slice().map(|slice| slice.iter())
}
pub fn as_real_slice(&self) -> Option<&[f64]> {
self.as_typed_slice()
}
pub fn as_real_iter(&self) -> Option<impl Iterator<Item = &f64>> {
self.as_real_slice().map(|slice| slice.iter())
}
pub fn as_real_vector(&self) -> Option<Vec<f64>> {
self.as_real_slice().map(|value| value.to_vec())
}
pub fn as_raw_slice(&self) -> Option<&[u8]> {
self.as_typed_slice()
}
pub fn as_integer_slice_mut(&mut self) -> Option<&mut [i32]> {
self.as_typed_slice_mut()
}
pub fn as_real_slice_mut(&mut self) -> Option<&mut [f64]> {
self.as_typed_slice_mut()
}
pub fn as_raw_slice_mut(&mut self) -> Option<&mut [u8]> {
self.as_typed_slice_mut()
}
pub fn as_string_vector(&self) -> Option<Vec<String>> {
self.as_str_iter()
.map(|iter| iter.map(str::to_string).collect())
}
pub fn as_str_vector(&self) -> Option<Vec<&str>> {
self.as_str_iter().map(|iter| iter.collect())
}
pub fn as_str<'a>(&self) -> Option<&'a str> {
unsafe {
let charsxp = match self.sexptype() {
STRSXP => {
if self.len() != 1 {
return None;
}
STRING_ELT(self.get(), 0)
}
CHARSXP => self.get(),
SYMSXP => PRINTNAME(self.get()),
_ => return None,
};
rstr::charsxp_to_str(charsxp)
}
}
pub fn as_integer(&self) -> Option<i32> {
match self.as_integer_slice() {
Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0]),
_ => None,
}
}
pub fn as_real(&self) -> Option<f64> {
match self.as_real_slice() {
Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0]),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self.as_logical_slice() {
Some(slice) if slice.len() == 1 && !slice[0].is_na() => Some(slice[0].is_true()),
_ => None,
}
}
pub fn as_logical(&self) -> Option<Rbool> {
match self.as_logical_slice() {
Some(slice) if slice.len() == 1 => Some(slice[0]),
_ => None,
}
}
}
pub trait Eval: GetSexp {
fn eval(&self) -> Result<Robj> {
self.eval_with_env(&global_env())
}
fn eval_with_env(&self, env: &Environment) -> Result<Robj> {
single_threaded(|| unsafe {
let mut error: raw::c_int = 0;
let res = R_tryEval(self.get(), env.get(), &mut error as *mut raw::c_int);
if error != 0 {
Err(Error::EvalError(Robj::from_sexp(self.get())))
} else {
Ok(Robj::from_sexp(res))
}
})
}
fn eval_blind(&self) -> Robj {
let res = self.eval();
if let Ok(robj) = res {
robj
} else {
Robj::from(())
}
}
}
impl Eval for Robj {}
pub trait AsTypedSlice<'a, T>
where
Self: 'a,
{
fn as_typed_slice(&self) -> Option<&'a [T]>
where
Self: 'a,
{
None
}
fn as_typed_slice_mut(&mut self) -> Option<&'a mut [T]>
where
Self: 'a,
{
None
}
}
macro_rules! make_typed_slice {
($type: ty, $fn: tt, $($sexp: tt),* ) => {
impl<'a> AsTypedSlice<'a, $type> for Robj
where
Self : 'a,
{
fn as_typed_slice(&self) -> Option<&'a [$type]> {
match self.sexptype() {
$( $sexp )|* => {
unsafe {
if self.is_empty() {
return Some(&[])
}
let ptr = $fn(self.get()) as *const $type;
Some(std::slice::from_raw_parts(ptr, self.len()))
}
}
_ => None
}
}
fn as_typed_slice_mut(&mut self) -> Option<&'a mut [$type]> {
match self.sexptype() {
$( $sexp )|* => {
unsafe {
if self.is_empty() {
return Some(&mut []);
}
let ptr = $fn(self.get_mut()) as *mut $type;
Some(std::slice::from_raw_parts_mut(ptr, self.len()))
}
}
_ => None
}
}
}
}
}
make_typed_slice!(Rbool, INTEGER, LGLSXP);
make_typed_slice!(i32, INTEGER, INTSXP);
make_typed_slice!(Rint, INTEGER, INTSXP);
make_typed_slice!(f64, REAL, REALSXP);
make_typed_slice!(Rfloat, REAL, REALSXP);
make_typed_slice!(u8, RAW, RAWSXP);
make_typed_slice!(Rstr, STRING_PTR_RO, STRSXP);
make_typed_slice!(c64, COMPLEX, CPLXSXP);
make_typed_slice!(Rcplx, COMPLEX, CPLXSXP);
make_typed_slice!(Rcomplex, COMPLEX, CPLXSXP);
#[allow(non_snake_case)]
pub trait Attributes: Types + Length {
fn get_attrib<'a, N>(&self, name: N) -> Option<Robj>
where
Self: 'a,
Robj: From<N> + 'a,
{
let name = Robj::from(name);
if self.sexptype() == SEXPTYPE::CHARSXP {
None
} else {
let res = unsafe { Robj::from_sexp(Rf_getAttrib(self.get(), name.get())) };
if res.is_null() {
None
} else {
Some(res)
}
}
}
fn has_attrib<'a, N>(&self, name: N) -> bool
where
Self: 'a,
Robj: From<N> + 'a,
{
let name = Robj::from(name);
if self.sexptype() == SEXPTYPE::CHARSXP {
false
} else {
unsafe { Rf_getAttrib(self.get(), name.get()) != R_NilValue }
}
}
fn set_attrib<N, V>(&mut self, name: N, value: V) -> Result<&mut Self>
where
N: Into<Robj>,
V: Into<Robj>,
{
let name = name.into();
let value = value.into();
unsafe {
let sexp = self.get_mut();
let result =
single_threaded(|| catch_r_error(|| Rf_setAttrib(sexp, name.get(), value.get())));
result.map(|_| self)
}
}
fn names(&self) -> Option<StrIter> {
if let Some(names) = self.get_attrib(wrapper::symbol::names_symbol()) {
names.as_str_iter()
} else {
None
}
}
fn has_names(&self) -> bool {
self.has_attrib(wrapper::symbol::names_symbol())
}
fn set_names<T>(&mut self, names: T) -> Result<&mut Self>
where
T: IntoIterator,
T::IntoIter: ExactSizeIterator,
T::Item: ToVectorValue + AsRef<str>,
{
let iter = names.into_iter();
let robj = iter.collect_robj();
if !robj.is_vector() && !robj.is_pairlist() {
Err(Error::ExpectedVector(robj))
} else if robj.len() != self.len() {
Err(Error::NamesLengthMismatch(robj))
} else {
self.set_attrib(wrapper::symbol::names_symbol(), robj)
}
}
fn dim(&self) -> Option<Integers> {
if let Some(dim) = self.get_attrib(wrapper::symbol::dim_symbol()) {
dim.as_integers()
} else {
None
}
}
fn dimnames(&self) -> Option<ListIter> {
if let Some(names) = self.get_attrib(wrapper::symbol::dimnames_symbol()) {
names.as_list().map(|v| v.values())
} else {
None
}
}
fn class(&self) -> Option<StrIter> {
if let Some(class) = self.get_attrib(wrapper::symbol::class_symbol()) {
class.as_str_iter()
} else {
None
}
}
fn set_class<T>(&mut self, class: T) -> Result<&mut Self>
where
T: IntoIterator,
T::IntoIter: ExactSizeIterator,
T::Item: ToVectorValue + AsRef<str>,
{
let iter = class.into_iter();
self.set_attrib(wrapper::symbol::class_symbol(), iter.collect_robj())
}
fn inherits(&self, classname: &str) -> bool {
if let Some(mut iter) = self.class() {
iter.any(|n| n == classname)
} else {
false
}
}
fn levels(&self) -> Option<StrIter> {
if let Some(levels) = self.get_attrib(wrapper::symbol::levels_symbol()) {
levels.as_str_iter()
} else {
None
}
}
}
impl Attributes for Robj {}
impl PartialEq<[i32]> for Robj {
fn eq(&self, rhs: &[i32]) -> bool {
self.as_integer_slice() == Some(rhs)
}
}
impl PartialEq<[f64]> for Robj {
fn eq(&self, rhs: &[f64]) -> bool {
self.as_real_slice() == Some(rhs)
}
}
impl PartialEq<str> for Robj {
fn eq(&self, rhs: &str) -> bool {
self.as_str() == Some(rhs)
}
}
impl PartialEq<Robj> for Robj {
fn eq(&self, rhs: &Robj) -> bool {
unsafe {
if self.get() == rhs.get() {
return true;
}
R_compute_identical(self.get(), rhs.get(), 16) != Rboolean::FALSE
}
}
}
impl Drop for Robj {
fn drop(&mut self) {
unsafe {
ownership::unprotect(self.inner);
}
}
}