1//! `ExternalPtr` is a way to leak Rust allocated data to R, forego deallocation
2//! to R and its GC strategy.
3//!
4//! An `ExternalPtr` encompasses three values, an owned pointer to the Rust
5//! type, a `tag` and a `prot`. Tag is a helpful naming of the type, but
6//! it doesn't offer any solid type-checking capability. And `prot` is meant
7//! to be R values, that are supposed to be kept together with the `ExternalPtr`.
8//!
9//! Neither `tag` nor `prot` are attributes, therefore to use `ExternalPtr` as
10//! a class in R, you must decorate it with a class-attribute manually.
11//!
12//! **Beware**: Equality (by way of `PartialEq`) does not imply equality of value,
13//! but equality of pointer. Two objects stored as `ExternalPtr` may be equal
14//! in value, but be two distinct entities, with distinct pointers.
15//!
16//! Instead, rely on `AsRef` to make _by value_ comparison, e.g. to compare
17//! for equality of
18//! two instances of `ExternalPtr<T>` by value, `a.as_ref() == b.as_ref()`.
19//!
20use super::*;
21use extendr_ffi::{
22 R_ClearExternalPtr, R_ExternalPtrAddr, R_ExternalPtrProtected, R_ExternalPtrTag,
23 R_MakeExternalPtr, R_NilValue, R_SetExternalPtrTag,
24};
25use std::{any::Any, fmt::Debug};
26/// Wrapper for creating R objects containing any Rust object.
27///
28/// ```
29/// use extendr_api::prelude::*;
30/// test! {
31/// let extptr = ExternalPtr::new(1);
32/// assert_eq!(*extptr, 1);
33/// let robj : Robj = extptr.into();
34/// let extptr2 : ExternalPtr<i32> = robj.try_into().unwrap();
35/// assert_eq!(*extptr2, 1);
36/// }
37/// ```
38#[repr(transparent)]
39pub struct ExternalPtr<T> {
40/// This is the contained Robj.
41pub(crate) robj: Robj,
4243/// This is a zero-length object that holds the type of the object.
44_marker: std::marker::PhantomData<T>,
45}
4647/// Manual implementation of `PartialEq`, because the constraint `T: PartialEq`
48/// is not necessary.
49impl<T> PartialEq for ExternalPtr<T> {
50fn eq(&self, other: &Self) -> bool {
51self.robj == other.robj && self._marker == other._marker
52 }
53}
5455/// Manual implementation of `Clone` trait, because the assumed constraint `T: Clone` is not necessary.
56impl<T> Clone for ExternalPtr<T> {
57fn clone(&self) -> Self {
58Self {
59 robj: self.robj.clone(),
60 _marker: self._marker,
61 }
62 }
63}
6465impl<T> robj::GetSexp for ExternalPtr<T> {
66unsafe fn get(&self) -> SEXP {
67self.robj.get()
68 }
6970unsafe fn get_mut(&mut self) -> SEXP {
71self.robj.get_mut()
72 }
7374/// Get a reference to a Robj for this type.
75fn as_robj(&self) -> &Robj {
76&self.robj
77 }
7879/// Get a mutable reference to a Robj for this type.
80fn as_robj_mut(&mut self) -> &mut Robj {
81&mut self.robj
82 }
83}
8485/// len() and is_empty()
86impl<T> Length for ExternalPtr<T> {}
8788/// rtype() and rany()
89impl<T> Types for ExternalPtr<T> {}
9091/// `set_attrib`
92impl<T> Attributes for ExternalPtr<T> {}
9394/// as_*()
95impl<T> Conversions for ExternalPtr<T> {}
9697/// find_var() etc.
98impl<T> Rinternals for ExternalPtr<T> {}
99100/// as_typed_slice_raw() etc.
101impl<T> Slices for ExternalPtr<T> {}
102103/// dollar() etc.
104impl<T> Operators for ExternalPtr<T> {}
105106impl<T: 'static> Deref for ExternalPtr<T> {
107type Target = T;
108109/// This allows us to treat the Robj as if it is the type T.
110fn deref(&self) -> &Self::Target {
111self.addr()
112 }
113}
114115impl<T: 'static> DerefMut for ExternalPtr<T> {
116/// This allows us to treat the Robj as if it is the mutable type T.
117fn deref_mut(&mut self) -> &mut Self::Target {
118self.addr_mut()
119 }
120}
121122impl<T: 'static> ExternalPtr<T> {
123/// Construct an external pointer object from any type T.
124 /// In this case, the R object owns the data and will drop the Rust object
125 /// when the last reference is removed via register_c_finalizer.
126 ///
127 /// An ExternalPtr behaves like a Box except that the information is
128 /// tracked by a R object.
129pub fn new(val: T) -> Self {
130 single_threaded(|| unsafe {
131// This allocates some memory for our object and moves the object into it.
132let boxed: Box<dyn Any> = Box::new(val);
133let boxed: Box<Box<dyn Any>> = Box::new(boxed);
134135// This constructs an external pointer to our boxed data.
136 // into_raw() converts the box to a malloced pointer.
137let robj = {
138let boxed_ptr = Box::into_raw(boxed);
139let prot = Robj::from(());
140let type_name: Robj = std::any::type_name::<T>().into();
141142 Robj::from_sexp(single_threaded(|| {
143 R_MakeExternalPtr(boxed_ptr.cast(), type_name.get(), prot.get())
144 }))
145 };
146147extern "C" fn finalizer(x: SEXP) {
148unsafe {
149let ptr = R_ExternalPtrAddr(x).cast::<Box<dyn Any>>();
150151// Free the `tag`, which is the type-name
152R_SetExternalPtrTag(x, R_NilValue);
153154// Convert the pointer to a box and drop it implictly.
155 // This frees up the memory we have used and calls the "T::drop" method if there is one.
156drop(Box::from_raw(ptr));
157158// Now set the pointer in ExternalPTR to C `NULL`
159R_ClearExternalPtr(x);
160 }
161 }
162163// Tell R about our finalizer
164robj.register_c_finalizer(Some(finalizer));
165166// Return an object in a wrapper.
167Self {
168 robj,
169 _marker: std::marker::PhantomData,
170 }
171 })
172 }
173174// TODO: make a constructor for references?
175176/// Get the "tag" of an external pointer. This is the type name in the common case.
177pub fn tag(&self) -> Robj {
178unsafe { Robj::from_sexp(R_ExternalPtrTag(self.robj.get())) }
179 }
180181/// Get the "protected" field of an external pointer. This is NULL in the common case.
182pub fn protected(&self) -> Robj {
183unsafe { Robj::from_sexp(R_ExternalPtrProtected(self.robj.get())) }
184 }
185186/// Get the "address" field of an external pointer.
187 /// Normally, we will use Deref to do this.
188 ///
189 /// ## Panics
190 ///
191 /// When the underlying pointer is C `NULL`.
192pub fn addr(&self) -> &T {
193self.try_addr().unwrap()
194 }
195196/// Get the "address" field of an external pointer as a mutable reference.
197 /// Normally, we will use DerefMut to do this.
198 ///
199 /// ## Panics
200 ///
201 /// When the underlying pointer is C `NULL`.
202pub fn addr_mut(&mut self) -> &mut T {
203self.try_addr_mut().unwrap()
204 }
205206/// Get the "address" field of an external pointer.
207 /// Normally, we will use Deref to do this.
208 ///
209 /// ## Panics
210 ///
211 /// When the underlying pointer is C `NULL`.
212pub fn try_addr(&self) -> Result<&T> {
213unsafe {
214 R_ExternalPtrAddr(self.robj.get())
215 .cast::<Box<dyn Any>>()
216 .as_ref()
217 .ok_or_else(|| Error::ExpectedExternalNonNullPtr(self.robj.clone()))
218 .map(|x| x.downcast_ref::<T>().unwrap())
219 }
220 }
221222/// Get the "address" field of an external pointer as a mutable reference.
223 /// Normally, we will use DerefMut to do this.
224 ///
225 /// ## Panics
226 ///
227 /// When the underlying pointer is C `NULL`.
228pub fn try_addr_mut(&mut self) -> Result<&mut T> {
229unsafe {
230 R_ExternalPtrAddr(self.robj.get_mut())
231 .cast::<Box<dyn Any>>()
232 .as_mut()
233 .ok_or_else(|| Error::ExpectedExternalNonNullPtr(self.robj.clone()))
234 .map(|x| x.downcast_mut::<T>().unwrap())
235 }
236 }
237}
238239impl<T: 'static> TryFrom<&Robj> for &ExternalPtr<T> {
240type Error = Error;
241242fn try_from(value: &Robj) -> Result<Self> {
243if !value.is_external_pointer() {
244return Err(Error::ExpectedExternalPtr(value.clone()));
245 }
246247// check type by downcasting
248let boxed_ptr = unsafe {
249 value
250 .external_ptr_addr::<Box<dyn Any>>()
251 .cast_const()
252 .as_ref()
253 .ok_or_else(|| Error::ExpectedExternalNonNullPtr(value.clone()))?
254};
255256if boxed_ptr.downcast_ref::<T>().is_none() {
257return Err(Error::ExpectedExternalPtrType(
258 value.clone(),
259 std::any::type_name::<T>().to_string(),
260 ));
261 }
262263unsafe { Ok(std::mem::transmute::<&Robj, &ExternalPtr<T>>(value)) }
264 }
265}
266267impl<T: 'static> TryFrom<&mut Robj> for &mut ExternalPtr<T> {
268type Error = Error;
269270fn try_from(value: &mut Robj) -> Result<Self> {
271if !value.is_external_pointer() {
272return Err(Error::ExpectedExternalPtr(value.clone()));
273 }
274275// check type by downcasting
276let boxed_ptr = unsafe {
277 value
278 .external_ptr_addr::<Box<dyn Any>>()
279 .cast_const()
280 .as_ref()
281 .ok_or_else(|| Error::ExpectedExternalNonNullPtr(value.clone()))?
282};
283284if boxed_ptr.downcast_ref::<T>().is_none() {
285return Err(Error::ExpectedExternalPtrType(
286 value.clone(),
287 std::any::type_name::<T>().to_string(),
288 ));
289 }
290291unsafe { Ok(std::mem::transmute::<&mut Robj, &mut ExternalPtr<T>>(value)) }
292 }
293}
294295impl<T: 'static> TryFrom<Robj> for &ExternalPtr<T> {
296type Error = Error;
297298fn try_from(value: Robj) -> Result<Self> {
299 (&value).try_into()
300 }
301}
302303impl<T: 'static> TryFrom<Robj> for &mut ExternalPtr<T> {
304type Error = Error;
305306fn try_from(mut value: Robj) -> Result<Self> {
307 (&mut value).try_into()
308 }
309}
310311impl<T: 'static> TryFrom<&Robj> for ExternalPtr<T> {
312type Error = Error;
313314fn try_from(robj: &Robj) -> Result<Self> {
315let result: &Self = robj.try_into()?;
316Ok(result.clone())
317 }
318}
319320impl<T: 'static> TryFrom<Robj> for ExternalPtr<T> {
321type Error = Error;
322323fn try_from(robj: Robj) -> Result<Self> {
324 <ExternalPtr<T>>::try_from(&robj)
325 }
326}
327328impl<T> From<ExternalPtr<T>> for Robj {
329fn from(val: ExternalPtr<T>) -> Self {
330 val.robj
331 }
332}
333334impl<T> From<Option<ExternalPtr<T>>> for Robj {
335fn from(value: Option<ExternalPtr<T>>) -> Self {
336match value {
337None => nil_value(),
338Some(value) => value.into(),
339 }
340 }
341}
342343impl<T: Debug + 'static> std::fmt::Debug for ExternalPtr<T> {
344fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345 (&**self as &T).fmt(f)
346 }
347}
348349impl<T: 'static> AsRef<T> for ExternalPtr<T> {
350fn as_ref(&self) -> &T {
351self.addr()
352 }
353}
354355impl<T: 'static> AsMut<T> for ExternalPtr<T> {
356fn as_mut(&mut self) -> &mut T {
357self.addr_mut()
358 }
359}
360361#[cfg(test)]
362mod tests {
363use super::*;
364use extendr_engine::with_r;
365366#[derive(Debug)]
367struct BareWrapper(i32);
368369#[test]
370fn externalptr_is_ptr() {
371 with_r(|| {
372let a = BareWrapper(42);
373let b = BareWrapper(42);
374assert_eq!(a.0, b.0);
375376let a_ptr = std::ptr::addr_of!(a);
377let b_ptr = std::ptr::addr_of!(b);
378let a_externalptr = ExternalPtr::new(a);
379let b_externalptr = ExternalPtr::new(b);
380381assert_ne!(
382 a_ptr, b_ptr,
383"pointers has to be equal by address, not value"
384);
385386assert_ne!(
387 a_externalptr.robj, b_externalptr.robj,
388"R only knows about the pointer, and not the pointee"
389);
390assert_ne!(
391 a_externalptr, b_externalptr,
392"ExternalPtr acts exactly like a pointer"
393);
394assert_ne!(&a_externalptr, &b_externalptr,);
395 });
396 }
397398#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
399struct Wrapper(i32);
400401#[test]
402fn compare_externalptr_pointee() {
403 with_r(|| {
404let a = Wrapper(42);
405let b = Wrapper(42);
406let a_externalptr = ExternalPtr::new(a);
407let b_externalptr = ExternalPtr::new(b);
408assert_eq!(a_externalptr.as_ref(), b_externalptr.as_ref());
409410// let's test more use of `PartialOrd` on `T`
411let a_externalptr = ExternalPtr::new(Wrapper(50));
412let b_externalptr = ExternalPtr::new(Wrapper(60));
413assert!(a_externalptr.as_ref() <= b_externalptr.as_ref());
414assert_eq!(
415 a_externalptr.as_ref().max(b_externalptr.as_ref()),
416&Wrapper(60)
417 )
418 });
419 }
420}