use savvy_ffi::{
R_NilValue, R_PreserveObject, Rf_cons, Rf_protect, Rf_unprotect, CAR, CDR, SETCAR, SETCDR,
SET_TAG, SEXP,
};
use std::sync::OnceLock;
pub(crate) struct LocalProtection {}
impl Drop for LocalProtection {
fn drop(&mut self) {
unsafe { Rf_unprotect(1) };
}
}
pub(crate) fn local_protect(obj: SEXP) -> LocalProtection {
unsafe { Rf_protect(obj) };
LocalProtection {}
}
pub(crate) struct PreservedList(SEXP);
unsafe impl Send for PreservedList {}
unsafe impl Sync for PreservedList {}
pub(crate) static PRESERVED_LIST: OnceLock<PreservedList> = OnceLock::new();
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn insert_to_preserved_list(obj: SEXP) -> SEXP {
unsafe {
if obj == R_NilValue {
return R_NilValue;
}
let _obj_guard = local_protect(obj);
let preserved = PRESERVED_LIST.get_or_init(|| {
let r = Rf_cons(R_NilValue, R_NilValue);
R_PreserveObject(r);
PreservedList(r)
});
let token = Rf_cons(preserved.0, CDR(preserved.0));
let _token_guard = local_protect(token);
SET_TAG(token, obj);
SETCDR(preserved.0, token);
if CDR(token) != R_NilValue {
SETCAR(CDR(token), token);
}
token
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn release_from_preserved_list(token: SEXP) {
unsafe {
if token == R_NilValue {
return;
}
let before = CAR(token);
let after = CDR(token);
SETCDR(before, after);
if after != R_NilValue {
SETCAR(after, before);
}
}
}