use rustler::{Encoder, Env, Term};
use std::collections::HashSet;
const RX_DECODE_MAX_DEPTH: usize = 100;
#[allow(dead_code)]
#[derive(Clone)]
pub enum TaggedValue {
Null,
Logical(bool),
Integer(i32),
Double(f64),
Character(Vec<u8>),
LogicalVector(Vec<TaggedValue>),
IntegerVector(Vec<TaggedValue>),
DoubleVector(Vec<TaggedValue>),
CharacterVector(Vec<TaggedValue>),
Na(crate::work::NaType),
NamedList(Vec<(Vec<u8>, TaggedValue)>),
RList(Vec<(Option<Vec<u8>>, TaggedValue)>),
Object(rustler::ResourceArc<crate::resource::RxResource>),
}
impl std::fmt::Debug for TaggedValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Null => f.write_str("Null"),
Self::Logical(value) => f.debug_tuple("Logical").field(value).finish(),
Self::Integer(value) => f.debug_tuple("Integer").field(value).finish(),
Self::Double(value) => f.debug_tuple("Double").field(value).finish(),
Self::Character(value) => f.debug_tuple("Character").field(value).finish(),
Self::LogicalVector(values) => f.debug_tuple("LogicalVector").field(values).finish(),
Self::IntegerVector(values) => f.debug_tuple("IntegerVector").field(values).finish(),
Self::DoubleVector(values) => f.debug_tuple("DoubleVector").field(values).finish(),
Self::CharacterVector(values) => {
f.debug_tuple("CharacterVector").field(values).finish()
}
Self::Na(value) => f.debug_tuple("Na").field(value).finish(),
Self::NamedList(entries) => f.debug_tuple("NamedList").field(entries).finish(),
Self::RList(entries) => f.debug_tuple("RList").field(entries).finish(),
Self::Object(_resource) => f.write_str("Object(..)"),
}
}
}
impl TaggedValue {
pub fn encode<'a>(&self, env: Env<'a>) -> Term<'a> {
match self {
Self::Null => (crate::atoms::null(), crate::atoms::nil()).encode(env),
Self::Logical(value) => (crate::atoms::logical(), *value).encode(env),
Self::Integer(value) => (crate::atoms::integer(), *value).encode(env),
Self::Double(value) => (crate::atoms::double(), *value).encode(env),
Self::Character(bytes) => {
let binary =
if let Some(mut out) = rustler::types::binary::OwnedBinary::new(bytes.len()) {
out.as_mut_slice().copy_from_slice(bytes);
out.release(env).encode(env)
} else {
"".encode(env)
};
(crate::atoms::character(), binary).encode(env)
}
Self::LogicalVector(values) => {
(crate::atoms::logical_vector(), encode_list(env, values)).encode(env)
}
Self::IntegerVector(values) => {
(crate::atoms::integer_vector(), encode_list(env, values)).encode(env)
}
Self::DoubleVector(values) => {
(crate::atoms::double_vector(), encode_list(env, values)).encode(env)
}
Self::CharacterVector(values) => {
(crate::atoms::character_vector(), encode_list(env, values)).encode(env)
}
Self::Na(na_type) => (crate::atoms::na(), encode_na_type(env, *na_type)).encode(env),
Self::NamedList(entries) => {
let entries = entries
.iter()
.map(|(name, value)| (crate::terms::binary(env, name), value.encode(env)))
.collect::<Vec<_>>();
(crate::atoms::named_list(), entries).encode(env)
}
Self::RList(entries) => {
let entries = entries
.iter()
.map(|(name, value)| {
let name = name
.as_ref()
.map(|name| crate::terms::binary(env, name))
.unwrap_or_else(|| crate::atoms::nil().encode(env));
(name, value.encode(env))
})
.collect::<Vec<_>>();
(crate::atoms::r_list(), entries).encode(env)
}
Self::Object(resource) => (crate::atoms::object(), resource).encode(env),
}
}
}
fn encode_list<'a>(env: Env<'a>, values: &[TaggedValue]) -> Vec<Term<'a>> {
values.iter().map(|value| value.encode(env)).collect()
}
fn encode_na_type<'a>(env: Env<'a>, na_type: crate::work::NaType) -> Term<'a> {
match na_type {
crate::work::NaType::Logical => crate::atoms::logical().encode(env),
crate::work::NaType::Integer => crate::atoms::integer().encode(env),
crate::work::NaType::Double => crate::atoms::double().encode(env),
crate::work::NaType::Character => crate::atoms::character().encode(env),
}
}
pub fn parse_encode_value(
kind: rustler::Term,
value: rustler::Term,
) -> Result<crate::work::EncodeValue, rustler::Error> {
let kind_name = if let Ok(atom) = kind.atom_to_string() {
atom
} else if let Ok(binary) = kind.decode::<String>() {
binary
} else {
return Err(rustler::Error::BadArg);
};
match kind_name.as_str() {
"null" => {
if value.atom_to_string().ok().as_deref() == Some("nil") {
Ok(crate::work::EncodeValue::Null)
} else {
Err(rustler::Error::BadArg)
}
}
"logical" => value
.decode::<bool>()
.map(crate::work::EncodeValue::Logical),
"integer" => value.decode::<i32>().map(crate::work::EncodeValue::Integer),
"double" => {
if value.is_float() {
value.decode::<f64>().map(crate::work::EncodeValue::Double)
} else {
Err(rustler::Error::BadArg)
}
}
"character" => value
.decode::<rustler::Binary>()
.map(|binary| crate::work::EncodeValue::Character(binary.as_slice().to_vec())),
"logical_vector" => parse_logical_vector(value),
"integer_vector" => parse_integer_vector(value),
"double_vector" => parse_double_vector(value),
"character_vector" => parse_character_vector(value),
"na" => parse_na_type(value).map(crate::work::EncodeValue::Na),
"named_list" => parse_named_list(value),
_ => Err(rustler::Error::BadArg),
}
}
fn parse_logical_vector(value: rustler::Term) -> Result<crate::work::EncodeValue, rustler::Error> {
let values = value.decode::<Vec<rustler::Term>>()?;
values
.into_iter()
.map(|term| term.decode::<bool>())
.collect::<Result<Vec<_>, _>>()
.map(crate::work::EncodeValue::LogicalVector)
}
fn parse_integer_vector(value: rustler::Term) -> Result<crate::work::EncodeValue, rustler::Error> {
let values = value.decode::<Vec<rustler::Term>>()?;
values
.into_iter()
.map(|term| {
if term.is_integer() {
term.decode::<i32>()
} else {
Err(rustler::Error::BadArg)
}
})
.collect::<Result<Vec<_>, _>>()
.map(crate::work::EncodeValue::IntegerVector)
}
fn parse_double_vector(value: rustler::Term) -> Result<crate::work::EncodeValue, rustler::Error> {
let values = value.decode::<Vec<rustler::Term>>()?;
values
.into_iter()
.map(|term| {
if term.is_float() {
term.decode::<f64>()
} else {
Err(rustler::Error::BadArg)
}
})
.collect::<Result<Vec<_>, _>>()
.map(crate::work::EncodeValue::DoubleVector)
}
fn parse_character_vector(
value: rustler::Term,
) -> Result<crate::work::EncodeValue, rustler::Error> {
let values = value.decode::<Vec<rustler::Term>>()?;
values
.into_iter()
.map(|term| {
term.decode::<rustler::Binary>()
.map(|binary| binary.as_slice().to_vec())
})
.collect::<Result<Vec<_>, _>>()
.map(crate::work::EncodeValue::CharacterVector)
}
fn parse_na_type(value: rustler::Term) -> Result<crate::work::NaType, rustler::Error> {
let type_name = if let Ok(atom) = value.atom_to_string() {
atom
} else if let Ok(binary) = value.decode::<String>() {
binary
} else {
return Err(rustler::Error::BadArg);
};
match type_name.as_str() {
"logical" => Ok(crate::work::NaType::Logical),
"integer" => Ok(crate::work::NaType::Integer),
"double" => Ok(crate::work::NaType::Double),
"character" => Ok(crate::work::NaType::Character),
_ => Err(rustler::Error::BadArg),
}
}
fn parse_named_list(value: rustler::Term) -> Result<crate::work::EncodeValue, rustler::Error> {
let values = value.decode::<Vec<rustler::Term>>()?;
let mut entries = Vec::with_capacity(values.len());
for entry in values {
let (name, resource) = entry.decode::<(
rustler::Binary,
rustler::ResourceArc<crate::resource::RxResource>,
)>()?;
entries.push((name.as_slice().to_vec(), resource));
}
Ok(crate::work::EncodeValue::NamedList(entries))
}
pub unsafe fn decode_simple_value(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
) -> Result<TaggedValue, String> {
let kind = (api.type_of)(value);
let len = (api.xlength)(value);
match (kind, len) {
(crate::r_api::NILSXP, _) => Ok(TaggedValue::Null),
(crate::r_api::LGLSXP, 1) => Ok(TaggedValue::Logical((api.logical_elt)(value, 0) == 1)),
(crate::r_api::INTSXP, 1) => Ok(TaggedValue::Integer((api.integer_elt)(value, 0))),
(crate::r_api::REALSXP, 1) => Ok(TaggedValue::Double((api.real_elt)(value, 0))),
(crate::r_api::STRSXP, 1) => {
copy_string(api, (api.string_elt)(value, 0)).map(TaggedValue::Character)
}
_ => Err(format!(
"R result type {kind} with length {len} is not supported by the native harness"
)),
}
}
pub fn do_encode_resource(
state: &mut crate::owner::OwnerState,
work: &crate::work::EncodeResourceWork,
) -> crate::work::WorkResult {
if let Some(failure) = state.terminal_init_failure() {
return crate::work::WorkResult::NativeInitFailed(failure);
}
if !state.is_initialized() {
return crate::work::WorkResult::Error("embedded R runtime is not initialized".to_owned());
}
let Some(api) = state.r.as_ref() else {
return crate::work::WorkResult::Error("embedded R API is not initialized".to_owned());
};
unsafe {
match allocate_resource(api, &work.value) {
Ok(resource) => crate::work::WorkResult::Resource(resource),
Err(message) => crate::work::WorkResult::Error(message),
}
}
}
pub unsafe fn preserved_resource(
api: &crate::r_api::RApi,
sexp: crate::r_api::Sexp,
kind: crate::resource::ResourceKind,
) -> rustler::ResourceArc<crate::resource::RxResource> {
(api.preserve_object)(sexp);
let resource = rustler::ResourceArc::new(crate::resource::RxResource::new(kind));
resource.state.lock().expect("resource mutex poisoned").sexp = Some(sexp);
resource
}
pub fn live_resource_sexp(
resource: &rustler::ResourceArc<crate::resource::RxResource>,
) -> Result<crate::r_api::Sexp, String> {
let state = resource
.state
.lock()
.map_err(|_error| "native R object resource mutex was poisoned".to_owned())?;
if resource
.release_enqueued
.load(std::sync::atomic::Ordering::SeqCst)
{
return Err("native R object resource has already been released".to_owned());
}
state
.sexp
.ok_or_else(|| "native R object resource has already been released".to_owned())
}
pub fn do_decode_resource(
state: &mut crate::owner::OwnerState,
work: &crate::work::DecodeResourceWork,
) -> crate::work::WorkResult {
if let Some(failure) = state.terminal_init_failure() {
return crate::work::WorkResult::NativeInitFailed(failure);
}
if !state.is_initialized() {
return crate::work::WorkResult::Error("embedded R runtime is not initialized".to_owned());
}
let Some(api) = state.r.as_ref() else {
return crate::work::WorkResult::Error("embedded R API is not initialized".to_owned());
};
let (sexp, resource_kind) = {
let state = match work.resource.state.lock() {
Ok(state) => state,
Err(_error) => {
return crate::work::WorkResult::Error(
"native R object resource mutex was poisoned".to_owned(),
)
}
};
if work
.resource
.release_enqueued
.load(std::sync::atomic::Ordering::SeqCst)
{
return crate::work::WorkResult::Error(
"native R object resource has already been released".to_owned(),
);
}
match state.sexp {
Some(sexp) => (sexp, state.kind),
None => {
return crate::work::WorkResult::Error(
"native R object resource has already been released".to_owned(),
)
}
}
};
unsafe {
match decode_resource_value(api, sexp, resource_kind) {
Ok(value) => crate::work::WorkResult::Decoded(value),
Err(message) => crate::work::WorkResult::TaggedError("unsupported_r_value", message),
}
}
}
unsafe fn allocate_resource(
api: &crate::r_api::RApi,
value: &crate::work::EncodeValue,
) -> Result<rustler::ResourceArc<crate::resource::RxResource>, String> {
let mut protect_count = 0;
let result = match value {
crate::work::EncodeValue::Null => Ok((*api.nil_value, crate::resource::ResourceKind::Null)),
crate::work::EncodeValue::Logical(value) => {
let sexp = (api.protect)((api.scalar_logical)(if *value { 1 } else { 0 }));
protect_count += 1;
Ok((sexp, crate::resource::ResourceKind::Logical))
}
crate::work::EncodeValue::Integer(value) => {
let sexp = (api.protect)((api.scalar_integer)(*value));
protect_count += 1;
Ok((sexp, crate::resource::ResourceKind::Integer))
}
crate::work::EncodeValue::Double(value) => {
let sexp = (api.protect)((api.scalar_real)(*value));
protect_count += 1;
Ok((sexp, crate::resource::ResourceKind::Double))
}
crate::work::EncodeValue::Character(bytes) => {
let string = bytes_to_c_string(bytes)?;
let chars = (api.protect)((api.mk_char_len_ce)(
string.as_ptr(),
bytes.len() as i32,
crate::r_api::CE_UTF8,
));
protect_count += 1;
let sexp = (api.protect)((api.scalar_string)(chars));
protect_count += 1;
Ok((sexp, crate::resource::ResourceKind::Character))
}
crate::work::EncodeValue::LogicalVector(values) => {
let sexp = (api.protect)((api.alloc_vector)(
crate::r_api::LGLSXP,
values.len() as isize,
));
protect_count += 1;
for (index, value) in values.iter().enumerate() {
(api.set_logical_elt)(sexp, index as isize, if *value { 1 } else { 0 });
}
Ok((sexp, crate::resource::ResourceKind::LogicalVector))
}
crate::work::EncodeValue::IntegerVector(values) => {
let sexp = (api.protect)((api.alloc_vector)(
crate::r_api::INTSXP,
values.len() as isize,
));
protect_count += 1;
for (index, value) in values.iter().enumerate() {
(api.set_integer_elt)(sexp, index as isize, *value);
}
Ok((sexp, crate::resource::ResourceKind::IntegerVector))
}
crate::work::EncodeValue::DoubleVector(values) => {
let sexp = (api.protect)((api.alloc_vector)(
crate::r_api::REALSXP,
values.len() as isize,
));
protect_count += 1;
for (index, value) in values.iter().enumerate() {
(api.set_real_elt)(sexp, index as isize, *value);
}
Ok((sexp, crate::resource::ResourceKind::DoubleVector))
}
crate::work::EncodeValue::CharacterVector(values) => {
let sexp = (api.protect)((api.alloc_vector)(
crate::r_api::STRSXP,
values.len() as isize,
));
protect_count += 1;
for (index, value) in values.iter().enumerate() {
let string = bytes_to_c_string(value)?;
let chars = (api.protect)((api.mk_char_len_ce)(
string.as_ptr(),
value.len() as i32,
crate::r_api::CE_UTF8,
));
(api.set_string_elt)(sexp, index as isize, chars);
(api.unprotect)(1);
}
Ok((sexp, crate::resource::ResourceKind::CharacterVector))
}
crate::work::EncodeValue::Na(na_type) => allocate_na(api, *na_type).map(|sexp| {
let kind = match na_type {
crate::work::NaType::Logical => crate::resource::ResourceKind::Logical,
crate::work::NaType::Integer => crate::resource::ResourceKind::Integer,
crate::work::NaType::Double => crate::resource::ResourceKind::Double,
crate::work::NaType::Character => crate::resource::ResourceKind::Character,
};
protect_count += 1;
(sexp, kind)
}),
crate::work::EncodeValue::NamedList(entries) => {
let sexp = (api.protect)((api.alloc_vector)(
crate::r_api::VECSXP,
entries.len() as isize,
));
protect_count += 1;
let names = (api.protect)((api.alloc_vector)(
crate::r_api::STRSXP,
entries.len() as isize,
));
protect_count += 1;
for (index, (name, resource)) in entries.iter().enumerate() {
let name_string = bytes_to_c_string(name)?;
let chars = (api.protect)((api.mk_char_len_ce)(
name_string.as_ptr(),
name.len() as i32,
crate::r_api::CE_UTF8,
));
(api.set_string_elt)(names, index as isize, chars);
(api.unprotect)(1);
let value = live_resource_sexp(resource)?;
(api.set_vector_elt)(sexp, index as isize, value);
}
(api.set_attrib)(sexp, *api.names_symbol, names);
Ok((sexp, crate::resource::ResourceKind::Generic))
}
};
match result {
Ok((sexp, kind)) => {
let resource = preserved_resource(api, sexp, kind);
(api.unprotect)(protect_count);
Ok(resource)
}
Err(message) => {
(api.unprotect)(protect_count);
Err(message)
}
}
}
unsafe fn allocate_na(
api: &crate::r_api::RApi,
na_type: crate::work::NaType,
) -> Result<crate::r_api::Sexp, String> {
let sexp = match na_type {
crate::work::NaType::Logical => (api.protect)((api.scalar_logical)(*api.na_int)),
crate::work::NaType::Integer => (api.protect)((api.scalar_integer)(*api.na_int)),
crate::work::NaType::Double => (api.protect)((api.scalar_real)(*api.na_real)),
crate::work::NaType::Character => (api.protect)((api.scalar_string)(*api.na_string)),
};
Ok(sexp)
}
fn bytes_to_c_string(bytes: &[u8]) -> Result<std::ffi::CString, String> {
std::ffi::CString::new(bytes)
.map_err(|error| format!("native character value contains NUL byte: {error}"))
}
unsafe fn decode_resource_value(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
resource_kind: crate::resource::ResourceKind,
) -> Result<TaggedValue, String> {
let kind = (api.type_of)(value);
let len = (api.xlength)(value);
if value == *api.nil_value || kind == crate::r_api::NILSXP {
return Ok(TaggedValue::Null);
}
match resource_kind {
crate::resource::ResourceKind::LogicalVector => {
return decode_logical_vector(api, value, len)
}
crate::resource::ResourceKind::IntegerVector => {
return decode_integer_vector(api, value, len)
}
crate::resource::ResourceKind::DoubleVector => {
return decode_double_vector(api, value, len)
}
crate::resource::ResourceKind::CharacterVector => {
return decode_character_vector(api, value, len)
}
_other => {}
}
decode_r_value(api, value, 0)
}
unsafe fn decode_r_value(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
depth: usize,
) -> Result<TaggedValue, String> {
let kind = (api.type_of)(value);
let len = (api.xlength)(value);
if value == *api.nil_value || kind == crate::r_api::NILSXP {
return Ok(TaggedValue::Null);
}
if depth >= RX_DECODE_MAX_DEPTH || is_semantic_object(api, value) {
return decode_opaque_value(api, value, depth, kind);
}
match kind {
crate::r_api::LGLSXP => decode_logical(api, value, len),
crate::r_api::INTSXP => decode_integer(api, value, len),
crate::r_api::REALSXP => decode_double(api, value, len),
crate::r_api::STRSXP => decode_character(api, value, len),
crate::r_api::VECSXP => decode_r_list(api, value, depth),
_ => decode_opaque_value(api, value, depth, kind),
}
}
unsafe fn decode_opaque_value(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
depth: usize,
kind: i32,
) -> Result<TaggedValue, String> {
if depth == 0 {
return Err(format!(
"R result type {kind} is not supported by the native harness"
));
}
Ok(TaggedValue::Object(preserved_resource(
api,
value,
crate::resource::ResourceKind::Generic,
)))
}
unsafe fn decode_r_list(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
depth: usize,
) -> Result<TaggedValue, String> {
let len = (api.xlength)(value);
let capacity = usize_len(len)?;
let names = (api.get_attrib)(value, *api.names_symbol);
let has_valid_names = names != *api.nil_value
&& (api.type_of)(names) == crate::r_api::STRSXP
&& (api.xlength)(names) == len;
let mut fully_named = capacity == 0 || has_valid_names;
let mut seen_names = HashSet::with_capacity(capacity);
let mut entries = Vec::with_capacity(capacity);
for index in 0..len {
let name = if has_valid_names {
copy_list_name(api, names, index)?
} else {
None
};
if let Some(name) = name.as_ref() {
if !seen_names.insert(name.clone()) {
fully_named = false;
}
} else {
fully_named = false;
}
let decoded = decode_r_value(api, (api.vector_elt)(value, index), depth + 1)?;
entries.push((name, decoded));
}
if fully_named {
Ok(TaggedValue::NamedList(
entries
.into_iter()
.map(|(name, value)| {
(
name.expect("fully named list entry missing name after validation"),
value,
)
})
.collect(),
))
} else {
Ok(TaggedValue::RList(entries))
}
}
unsafe fn copy_list_name(
api: &crate::r_api::RApi,
names: crate::r_api::Sexp,
index: isize,
) -> Result<Option<Vec<u8>>, String> {
let name = (api.string_elt)(names, index);
if name == *api.na_string {
return Ok(None);
}
let bytes = copy_string(api, name)?;
if bytes.is_empty() {
Ok(None)
} else {
Ok(Some(bytes))
}
}
unsafe fn is_semantic_object(api: &crate::r_api::RApi, value: crate::r_api::Sexp) -> bool {
has_non_empty_attribute(api, value, *api.class_symbol)
|| has_non_empty_attribute(api, value, *api.dim_symbol)
}
unsafe fn has_non_empty_attribute(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
symbol: crate::r_api::Sexp,
) -> bool {
if symbol.is_null() || value == *api.nil_value {
return false;
}
let attr = (api.get_attrib)(value, symbol);
attr != *api.nil_value && (api.xlength)(attr) > 0
}
unsafe fn decode_logical(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
len: isize,
) -> Result<TaggedValue, String> {
if len == 1 {
return Ok(tagged_logical(api, (api.logical_elt)(value, 0)));
}
decode_logical_vector(api, value, len)
}
unsafe fn decode_logical_vector(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
len: isize,
) -> Result<TaggedValue, String> {
let mut values = Vec::with_capacity(usize_len(len)?);
for index in 0..len {
values.push(tagged_logical(api, (api.logical_elt)(value, index)));
}
Ok(TaggedValue::LogicalVector(values))
}
unsafe fn decode_integer(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
len: isize,
) -> Result<TaggedValue, String> {
if len == 1 {
return Ok(tagged_integer(api, (api.integer_elt)(value, 0)));
}
decode_integer_vector(api, value, len)
}
unsafe fn decode_integer_vector(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
len: isize,
) -> Result<TaggedValue, String> {
let mut values = Vec::with_capacity(usize_len(len)?);
for index in 0..len {
values.push(tagged_integer(api, (api.integer_elt)(value, index)));
}
Ok(TaggedValue::IntegerVector(values))
}
unsafe fn decode_double(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
len: isize,
) -> Result<TaggedValue, String> {
if len == 1 {
return Ok(tagged_double((api.real_elt)(value, 0)));
}
decode_double_vector(api, value, len)
}
unsafe fn decode_double_vector(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
len: isize,
) -> Result<TaggedValue, String> {
let mut values = Vec::with_capacity(usize_len(len)?);
for index in 0..len {
values.push(tagged_double((api.real_elt)(value, index)));
}
Ok(TaggedValue::DoubleVector(values))
}
unsafe fn decode_character(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
len: isize,
) -> Result<TaggedValue, String> {
if len == 1 {
return tagged_character(api, (api.string_elt)(value, 0));
}
decode_character_vector(api, value, len)
}
unsafe fn decode_character_vector(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
len: isize,
) -> Result<TaggedValue, String> {
let mut values = Vec::with_capacity(usize_len(len)?);
for index in 0..len {
values.push(tagged_character(api, (api.string_elt)(value, index))?);
}
Ok(TaggedValue::CharacterVector(values))
}
unsafe fn tagged_logical(api: &crate::r_api::RApi, value: i32) -> TaggedValue {
if value == *api.na_int {
TaggedValue::Na(crate::work::NaType::Logical)
} else {
TaggedValue::Logical(value == 1)
}
}
unsafe fn tagged_integer(api: &crate::r_api::RApi, value: i32) -> TaggedValue {
if value == *api.na_int {
TaggedValue::Na(crate::work::NaType::Integer)
} else {
TaggedValue::Integer(value)
}
}
fn tagged_double(value: f64) -> TaggedValue {
if value.is_nan() {
TaggedValue::Na(crate::work::NaType::Double)
} else {
TaggedValue::Double(value)
}
}
unsafe fn tagged_character(
api: &crate::r_api::RApi,
value: crate::r_api::Sexp,
) -> Result<TaggedValue, String> {
if value == *api.na_string {
Ok(TaggedValue::Na(crate::work::NaType::Character))
} else {
copy_string(api, value).map(TaggedValue::Character)
}
}
fn usize_len(len: isize) -> Result<usize, String> {
usize::try_from(len).map_err(|_| "native vector result is too large".to_owned())
}
pub unsafe fn copy_string(
api: &crate::r_api::RApi,
string: crate::r_api::Sexp,
) -> Result<Vec<u8>, String> {
let ptr = (api.r_char)(string);
if ptr.is_null() {
return Ok(Vec::new());
}
let len = (api.length)(string);
if len <= 0 {
return Ok(Vec::new());
}
let bytes = std::slice::from_raw_parts(ptr.cast::<u8>(), len as usize);
Ok(bytes.to_vec())
}