Skip to main content

extendr_api/wrapper/
altrep.rs

1use super::*;
2use extendr_ffi::*;
3use prelude::{Rbool, Rcplx, Rfloat, Rint, Scalar};
4
5macro_rules! make_from_iterator_impl {
6    ($impl : ident, $scalar_type : ident) => {
7        impl<Iter: ExactSizeIterator + std::fmt::Debug + Clone> $impl for Iter
8        where
9            Iter::Item: Into<$scalar_type>,
10        {
11            fn elt(&self, index: usize) -> $scalar_type {
12                $scalar_type::from(self.clone().nth(index).unwrap().into())
13            }
14
15            fn get_region(&self, index: usize, data: &mut [$scalar_type]) -> usize {
16                let len = self.len();
17                if index > len {
18                    0
19                } else {
20                    let mut iter = self.clone().skip(index);
21                    let num_elems = data.len().min(len - index);
22                    let dest = &mut data[0..num_elems];
23                    for d in dest.iter_mut() {
24                        *d = $scalar_type::from(iter.next().unwrap().into());
25                    }
26                    num_elems
27                }
28            }
29        }
30    };
31}
32
33macro_rules! make_from_iterator {
34    ($fn_name : ident, $make_class : ident, $impl : ident, $scalar_type : ident, $prim_type : ty) => {
35        pub fn $fn_name<Iter>(iter: Iter) -> Altrep
36        where
37            Iter: ExactSizeIterator + std::fmt::Debug + Clone + 'static + std::any::Any,
38            Iter::Item: Into<$scalar_type>,
39        {
40            let class = Altrep::$make_class::<Iter>(std::any::type_name::<Iter>(), "extendr");
41            let robj: Robj = Altrep::from_state_and_class(iter, class, false).into();
42            Altrep { robj }
43        }
44    };
45}
46
47#[derive(PartialEq, Clone)]
48pub struct Altrep {
49    pub(crate) robj: Robj,
50}
51
52/// Rust trait for implementing ALTREP.
53/// Implement one or more of these methods to generate an Altrep class.
54/// This is likely to be unstable for a while.
55pub trait AltrepImpl: Clone + std::fmt::Debug {
56    #[cfg(feature = "non-api")]
57    /// Constructor that is called when loading an Altrep object from a file.
58    ///
59    /// # Safety
60    ///
61    /// Access to a raw SEXP pointer can cause undefined behaviour and is not thread safe.
62    /// Note that we use a thread lock to ensure this doesn't occur.
63    unsafe fn unserialize_ex(
64        class: Robj,
65        state: Robj,
66        attributes: Robj,
67        obj_flags: i32,
68        levels: i32,
69    ) -> Robj {
70        use extendr_ffi::{SETLEVELS, SET_ATTRIB, SET_OBJECT};
71        let res = Self::unserialize(class, state);
72        if !res.is_null() {
73            single_threaded(|| unsafe {
74                let val = res.get();
75                SET_ATTRIB(val, attributes.get());
76                SET_OBJECT(val, obj_flags);
77                SETLEVELS(val, levels);
78            })
79        }
80        res
81    }
82
83    /// Simplified constructor that is called when loading an Altrep object from a file.
84    fn unserialize(_class: Robj, _state: Robj) -> Robj {
85        // We plan to hadle this via Serde by November.
86        ().into()
87    }
88
89    /// Fetch the state of this object when writing to a file.
90    fn serialized_state(_x: SEXP) -> Robj {
91        // We plan to hadle this via Serde by November.
92        ().into()
93    }
94
95    /// Duplicate this object, possibly duplicating attributes.
96    /// Currently this manifests the array but preserves the original object.
97    fn duplicate_ex(x: SEXP, deep: bool) -> Robj {
98        Self::duplicate(x, deep)
99    }
100
101    /// Duplicate this object. Called by Rf_duplicate.
102    /// Currently this manifests the array but preserves the original object.
103    fn duplicate(x: SEXP, _deep: bool) -> Robj {
104        Robj::from_sexp(manifest(x))
105    }
106
107    /// Coerce this object into some other type, if possible.
108    fn coerce(_x: SEXP, _ty: Rtype) -> Robj {
109        ().into()
110    }
111
112    /// Print the text for .Internal(inspect(obj))
113    fn inspect(
114        &self,
115        _pre: i32,
116        _deep: bool,
117        _pvec: i32, // _inspect_subtree: fn(robj: Robj, pre: i32, deep: i32, pvec: i32),
118    ) -> bool {
119        rprintln!("{:?}", self);
120        true
121    }
122
123    /// Get the virtual length of the vector.
124    /// For example for a compact range, return end - start + 1.
125    fn length(&self) -> usize;
126
127    /// Get the data pointer for this vector, possibly expanding the
128    /// compact representation into a full R vector.
129    ///
130    /// # Safety
131    ///
132    /// This function dereferences a raw SEXP pointer.
133    /// The caller must ensure that `x` is a valid SEXP pointer.
134    // Kept a safe `fn` on the 0.8 line for backwards compatibility; 0.9 makes it `unsafe`.
135    #[allow(clippy::not_unsafe_ptr_arg_deref)]
136    fn dataptr(x: SEXP, _writeable: bool) -> *mut u8 {
137        single_threaded(|| unsafe {
138            let data2 = R_altrep_data2(x);
139            if data2 == R_NilValue || TYPEOF(data2) != TYPEOF(x) {
140                let data2 = manifest(x);
141                R_set_altrep_data2(x, data2);
142                dataptr(data2) as *mut u8
143            } else {
144                dataptr(data2) as *mut u8
145            }
146        })
147    }
148
149    /// Get the data pointer for this vector, returning NULL
150    /// if the object is unmaterialized.
151    ///
152    /// # Safety
153    ///
154    /// This function dereferences a raw SEXP pointer.
155    /// The caller must ensure that `x` is a valid SEXP pointer.
156    // Kept a safe `fn` on the 0.8 line for backwards compatibility; 0.9 makes it `unsafe`.
157    #[allow(clippy::not_unsafe_ptr_arg_deref)]
158    fn dataptr_or_null(x: SEXP) -> *const u8 {
159        unsafe {
160            let data2 = R_altrep_data2(x);
161            if data2 == R_NilValue || TYPEOF(data2) != TYPEOF(x) {
162                std::ptr::null()
163            } else {
164                dataptr(data2) as *const u8
165            }
166        }
167    }
168
169    /// Implement subsetting (eg. `x[10:19]`) for this Altrep vector.
170    fn extract_subset(_x: Robj, _indx: Robj, _call: Robj) -> Robj {
171        // only available in later versions of R.
172        // x.extract_subset(indx, call)
173        Robj::from(())
174    }
175}
176
177// Manifest a vector by storing the "elt" values to memory.
178// Return the new vector.
179fn manifest(x: SEXP) -> SEXP {
180    single_threaded(|| unsafe {
181        Rf_protect(x);
182        let len = XLENGTH(x);
183        let data2 = Rf_allocVector(TYPEOF(x), len as R_xlen_t);
184        Rf_protect(data2);
185        match TYPEOF(x) {
186            SEXPTYPE::INTSXP => {
187                INTEGER_GET_REGION(x, 0, len as R_xlen_t, INTEGER(data2));
188            }
189            SEXPTYPE::LGLSXP => {
190                LOGICAL_GET_REGION(x, 0, len as R_xlen_t, LOGICAL(data2));
191            }
192            SEXPTYPE::REALSXP => {
193                REAL_GET_REGION(x, 0, len as R_xlen_t, REAL(data2));
194            }
195            SEXPTYPE::RAWSXP => {
196                RAW_GET_REGION(x, 0, len as R_xlen_t, RAW(data2));
197            }
198            SEXPTYPE::CPLXSXP => {
199                COMPLEX_GET_REGION(x, 0, len as R_xlen_t, COMPLEX(data2));
200            }
201            _ => {
202                Rf_unprotect(2);
203                panic!("unsupported ALTREP type.")
204            }
205        };
206        Rf_unprotect(2);
207        data2
208    })
209}
210
211pub trait AltIntegerImpl: AltrepImpl {
212    fn tot_min_max_nas(&self) -> (i64, i32, i32, usize, usize) {
213        let len = self.length();
214        let mut tot = 0;
215        let mut nas = 0;
216        let mut min = i32::MAX;
217        let mut max = i32::MIN;
218        for i in 0..len {
219            let val = self.elt(i);
220            if !val.is_na() {
221                tot += val.inner() as i64;
222                min = min.min(val.inner());
223                max = max.max(val.inner());
224                nas += 1;
225            }
226        }
227        (tot, min, max, len - nas, len)
228    }
229
230    /// Get a single element from this vector.
231    fn elt(&self, _index: usize) -> Rint;
232
233    /// Get a multiple elements from this vector.
234    fn get_region(&self, index: usize, data: &mut [Rint]) -> usize {
235        let len = self.length();
236        if index > len {
237            0
238        } else {
239            let num_elems = data.len().min(len - index);
240            let dest = &mut data[0..num_elems];
241            for (i, d) in dest.iter_mut().enumerate() {
242                *d = self.elt(i + index);
243            }
244            num_elems
245        }
246    }
247
248    /// Return TRUE if this vector is sorted, FALSE if not and Rbool::na() if unknown.
249    fn is_sorted(&self) -> Rbool {
250        Rbool::na()
251    }
252
253    /// Return true if this vector does not contain NAs.
254    fn no_na(&self) -> bool {
255        false
256    }
257
258    /// Return the sum of the elements in this vector.
259    /// If remove_nas is true, skip and NA values.
260    fn sum(&self, remove_nas: bool) -> Robj {
261        let (tot, _min, _max, nas, _len) = self.tot_min_max_nas();
262        if !remove_nas && nas != 0 {
263            NA_INTEGER.into()
264        } else {
265            tot.into()
266        }
267    }
268
269    /// Return the minimum of the elements in this vector.
270    /// If remove_nas is true, skip and NA values.
271    fn min(&self, remove_nas: bool) -> Robj {
272        let (_tot, min, _max, nas, len) = self.tot_min_max_nas();
273        if !remove_nas && nas != 0 || remove_nas && nas == len {
274            NA_INTEGER.into()
275        } else {
276            min.into()
277        }
278    }
279
280    /// Return the maximum of the elements in this vector.
281    /// If remove_nas is true, skip and NA values.
282    fn max(&self, remove_nas: bool) -> Robj {
283        let (_tot, _min, max, nas, len) = self.tot_min_max_nas();
284        if !remove_nas && nas != 0 || remove_nas && nas == len {
285            NA_INTEGER.into()
286        } else {
287            max.into()
288        }
289    }
290}
291
292pub trait AltRealImpl: AltrepImpl {
293    fn tot_min_max_nas(&self) -> (f64, f64, f64, usize, usize) {
294        let len = self.length();
295        let mut tot = 0.0;
296        let mut nas = 0;
297        let mut min = f64::MAX;
298        let mut max = f64::MIN;
299        for i in 0..len {
300            let val = self.elt(i);
301            if !val.is_na() {
302                tot += val.inner();
303                min = min.min(val.inner());
304                max = max.max(val.inner());
305                nas += 1;
306            }
307        }
308        (tot, min, max, len - nas, len)
309    }
310
311    /// Get a single element from this vector.
312    fn elt(&self, _index: usize) -> Rfloat;
313
314    /// Get a multiple elements from this vector.
315    fn get_region(&self, index: usize, data: &mut [Rfloat]) -> usize {
316        let len = self.length();
317        if index > len {
318            0
319        } else {
320            let num_elems = data.len().min(len - index);
321            let dest = &mut data[0..num_elems];
322            for (i, d) in dest.iter_mut().enumerate() {
323                *d = self.elt(i + index);
324            }
325            num_elems
326        }
327    }
328
329    /// Return TRUE if this vector is sorted, FALSE if not and Rbool::na() if unknown.
330    fn is_sorted(&self) -> Rbool {
331        Rbool::na()
332    }
333
334    /// Return true if this vector does not contain NAs.
335    fn no_na(&self) -> bool {
336        false
337    }
338
339    /// Return the sum of the elements in this vector.
340    /// If remove_nas is true, skip and NA values.
341    fn sum(&self, remove_nas: bool) -> Robj {
342        let (tot, _min, _max, nas, _len) = self.tot_min_max_nas();
343        if !remove_nas && nas != 0 {
344            NA_REAL.into()
345        } else {
346            tot.into()
347        }
348    }
349
350    /// Return the minimum of the elements in this vector.
351    /// If remove_nas is true, skip and NA values.
352    fn min(&self, remove_nas: bool) -> Robj {
353        let (_tot, min, _max, nas, len) = self.tot_min_max_nas();
354        if !remove_nas && nas != 0 || remove_nas && nas == len {
355            NA_REAL.into()
356        } else {
357            min.into()
358        }
359    }
360
361    /// Return the maximum of the elements in this vector.
362    /// If remove_nas is true, skip and NA values.
363    fn max(&self, remove_nas: bool) -> Robj {
364        let (_tot, _min, max, nas, len) = self.tot_min_max_nas();
365        if !remove_nas && nas != 0 || remove_nas && nas == len {
366            NA_REAL.into()
367        } else {
368            max.into()
369        }
370    }
371}
372
373pub trait AltLogicalImpl: AltrepImpl {
374    fn tot_min_max_nas(&self) -> (i64, i32, i32, usize, usize) {
375        let len = self.length();
376        let mut tot = 0;
377        let mut nas = 0;
378        for i in 0..len {
379            let val = self.elt(i);
380            if !val.is_na() {
381                tot += val.inner() as i64;
382                nas += 1;
383            }
384        }
385        (tot, 0, 0, len - nas, len)
386    }
387
388    /// Get a single element from this vector.
389    fn elt(&self, _index: usize) -> Rbool;
390
391    /// Get a multiple elements from this vector.
392    fn get_region(&self, index: usize, data: &mut [Rbool]) -> usize {
393        let len = self.length();
394        if index > len {
395            0
396        } else {
397            let num_elems = data.len().min(len - index);
398            let dest = &mut data[0..num_elems];
399            for (i, d) in dest.iter_mut().enumerate() {
400                *d = self.elt(i + index);
401            }
402            num_elems
403        }
404    }
405
406    /// Return TRUE if this vector is sorted, FALSE if not and Rbool::na() if unknown.
407    fn is_sorted(&self) -> Rbool {
408        Rbool::na()
409    }
410
411    /// Return true if this vector does not contain NAs.
412    fn no_na(&self) -> bool {
413        false
414    }
415
416    /// Return the sum of the elements in this vector.
417    /// If remove_nas is true, skip and NA values.
418    fn sum(&self, remove_nas: bool) -> Robj {
419        let (tot, _min, _max, nas, len) = self.tot_min_max_nas();
420        if !remove_nas && nas != 0 || remove_nas && nas == len {
421            Rbool::na().into()
422        } else {
423            tot.into()
424        }
425    }
426}
427
428pub trait AltRawImpl: AltrepImpl {
429    /// Get a single element from this vector.
430    fn elt(&self, _index: usize) -> u8;
431
432    /// Get a multiple elements from this vector.
433    fn get_region(&self, index: usize, data: &mut [u8]) -> usize {
434        let len = self.length();
435        if index > len {
436            0
437        } else {
438            let num_elems = data.len().min(len - index);
439            let dest = &mut data[0..num_elems];
440            for (i, d) in dest.iter_mut().enumerate() {
441                *d = self.elt(i + index);
442            }
443            num_elems
444        }
445    }
446}
447
448pub trait AltComplexImpl: AltrepImpl {
449    /// Get a single element from this vector.
450    fn elt(&self, _index: usize) -> Rcplx;
451
452    /// Get a multiple elements from this vector.
453    fn get_region(&self, index: usize, data: &mut [Rcplx]) -> usize {
454        let len = self.length();
455        if index > len {
456            0
457        } else {
458            let num_elems = data.len().min(len - index);
459            let dest = &mut data[0..num_elems];
460            for (i, d) in dest.iter_mut().enumerate() {
461                *d = self.elt(i + index);
462            }
463            num_elems
464        }
465    }
466}
467
468// Implement the trait methods for iterators
469make_from_iterator_impl!(AltIntegerImpl, Rint);
470make_from_iterator_impl!(AltLogicalImpl, Rbool);
471make_from_iterator_impl!(AltRealImpl, Rfloat);
472make_from_iterator_impl!(AltComplexImpl, Rcplx);
473
474pub trait AltStringImpl {
475    /// Get a single element from this vector.
476    fn elt(&self, _index: usize) -> Rstr;
477
478    /// Set a single element in this vector.
479    fn set_elt(&mut self, _index: usize, _value: Rstr) {}
480
481    /// Return TRUE if this vector is sorted, FALSE if not and Rbool::na() if unknown.
482    fn is_sorted(&self) -> Rbool {
483        Rbool::na()
484    }
485
486    /// Return true if this vector does not contain NAs.
487    fn no_na(&self) -> bool {
488        false
489    }
490}
491
492#[cfg(use_r_altlist)]
493pub trait AltListImpl {
494    /// Get a single element from this vector
495    /// a single element of a list can be any Robj
496    fn elt(&self, _index: usize) -> Robj;
497
498    /// Set a single element in this list.
499    fn set_elt(&mut self, _index: usize, _value: Robj) {}
500}
501
502impl Altrep {
503    /// Safely implement R_altrep_data1, R_altrep_data2.
504    /// When implementing Altrep classes, this gets the metadata.
505    pub fn data(&self) -> (Robj, Robj) {
506        unsafe {
507            (
508                Robj::from_sexp(R_altrep_data1(self.robj.get())),
509                Robj::from_sexp(R_altrep_data1(self.robj.get())),
510            )
511        }
512    }
513
514    /// Safely (relatively!) implement R_set_altrep_data1, R_set_altrep_data2.
515    /// When implementing Altrep classes, this sets the metadata.
516    pub fn set_data(&mut self, values: (Robj, Robj)) {
517        unsafe {
518            R_set_altrep_data1(self.robj.get(), values.0.get());
519            R_set_altrep_data2(self.robj.get(), values.1.get());
520        }
521    }
522
523    /// Safely implement ALTREP_CLASS.
524    pub fn class(&self) -> Robj {
525        single_threaded(|| unsafe { Robj::from_sexp(ALTREP_CLASS(self.robj.get())) })
526    }
527
528    pub fn from_state_and_class<StateType: 'static>(
529        state: StateType,
530        class: Robj,
531        mutable: bool,
532    ) -> Altrep {
533        single_threaded(|| unsafe {
534            use std::os::raw::c_void;
535
536            unsafe extern "C" fn finalizer<StateType: 'static>(x: SEXP) {
537                let state = R_ExternalPtrAddr(x);
538                let ptr = state as *mut StateType;
539                drop(Box::from_raw(ptr));
540            }
541
542            let ptr: *mut StateType = Box::into_raw(Box::new(state));
543            let tag = R_NilValue;
544            let prot = R_NilValue;
545            let state = R_MakeExternalPtr(ptr as *mut c_void, tag, prot);
546
547            // Use R_RegisterCFinalizerEx() and set onexit to 1 (TRUE) to invoke
548            // the finalizer on a shutdown of the R session as well.
549            R_RegisterCFinalizerEx(state, Some(finalizer::<StateType>), Rboolean::TRUE);
550
551            let class_ptr = R_altrep_class_t { ptr: class.get() };
552            let sexp = R_new_altrep(class_ptr, state, R_NilValue);
553
554            if !mutable {
555                MARK_NOT_MUTABLE(sexp);
556            }
557
558            Altrep {
559                robj: Robj::from_sexp(sexp),
560            }
561        })
562    }
563
564    /// Return true if the ALTREP object has been manifested (copied into memory).
565    pub fn is_manifest(&self) -> bool {
566        unsafe { !DATAPTR_OR_NULL(self.get()).is_null() }
567    }
568
569    #[allow(dead_code)]
570    pub(crate) fn get_state<StateType>(x: SEXP) -> &'static StateType {
571        unsafe {
572            let state_ptr = R_ExternalPtrAddr(R_altrep_data1(x));
573            &*(state_ptr as *const StateType)
574        }
575    }
576
577    #[allow(dead_code)]
578    pub(crate) fn get_state_mut<StateType>(x: SEXP) -> &'static mut StateType {
579        unsafe {
580            let state_ptr = R_ExternalPtrAddr(R_altrep_data1(x));
581            &mut *(state_ptr as *mut StateType)
582        }
583    }
584
585    fn altrep_class<StateType: AltrepImpl + 'static>(ty: Rtype, name: &str, base: &str) -> Robj {
586        #![allow(non_snake_case)]
587        #![allow(unused_variables)]
588        use std::os::raw::c_int;
589        use std::os::raw::c_void;
590
591        #[cfg(feature = "non-api")]
592        unsafe extern "C" fn altrep_UnserializeEX<StateType: AltrepImpl>(
593            class: SEXP,
594            state: SEXP,
595            attr: SEXP,
596            objf: c_int,
597            levs: c_int,
598        ) -> SEXP {
599            <StateType>::unserialize_ex(
600                Robj::from_sexp(class),
601                Robj::from_sexp(state),
602                Robj::from_sexp(attr),
603                objf,
604                levs,
605            )
606            .get()
607        }
608
609        unsafe extern "C" fn altrep_Unserialize<StateType: AltrepImpl + 'static>(
610            class: SEXP,
611            state: SEXP,
612        ) -> SEXP {
613            <StateType>::unserialize(Robj::from_sexp(class), Robj::from_sexp(state)).get()
614        }
615
616        unsafe extern "C" fn altrep_Serialized_state<StateType: AltrepImpl + 'static>(
617            x: SEXP,
618        ) -> SEXP {
619            <StateType>::serialized_state(x).get()
620        }
621
622        unsafe extern "C" fn altrep_Coerce<StateType: AltrepImpl + 'static>(
623            x: SEXP,
624            ty: SEXPTYPE,
625        ) -> SEXP {
626            <StateType>::coerce(x, sxp_to_rtype(ty)).get()
627        }
628
629        unsafe extern "C" fn altrep_Duplicate<StateType: AltrepImpl + 'static>(
630            x: SEXP,
631            deep: Rboolean,
632        ) -> SEXP {
633            <StateType>::duplicate(x, deep == Rboolean::TRUE).get()
634        }
635
636        unsafe extern "C" fn altrep_DuplicateEX<StateType: AltrepImpl + 'static>(
637            x: SEXP,
638            deep: Rboolean,
639        ) -> SEXP {
640            <StateType>::duplicate_ex(x, deep == Rboolean::TRUE).get()
641        }
642
643        unsafe extern "C" fn altrep_Inspect<StateType: AltrepImpl + 'static>(
644            x: SEXP,
645            pre: c_int,
646            deep: c_int,
647            pvec: c_int,
648            func: Option<unsafe extern "C" fn(arg1: SEXP, arg2: c_int, arg3: c_int, arg4: c_int)>,
649        ) -> Rboolean {
650            Altrep::get_state::<StateType>(x)
651                .inspect(pre, deep == 1, pvec)
652                .into()
653        }
654
655        unsafe extern "C" fn altrep_Length<StateType: AltrepImpl + 'static>(x: SEXP) -> R_xlen_t {
656            Altrep::get_state::<StateType>(x).length() as R_xlen_t
657        }
658
659        unsafe extern "C" fn altvec_Dataptr<StateType: AltrepImpl + 'static>(
660            x: SEXP,
661            writeable: Rboolean,
662        ) -> *mut c_void {
663            <StateType>::dataptr(x, writeable != Rboolean::FALSE) as *mut c_void
664        }
665
666        unsafe extern "C" fn altvec_Dataptr_or_null<StateType: AltrepImpl + 'static>(
667            x: SEXP,
668        ) -> *const c_void {
669            <StateType>::dataptr_or_null(x) as *mut c_void
670        }
671
672        unsafe extern "C" fn altvec_Extract_subset<StateType: AltrepImpl + 'static>(
673            x: SEXP,
674            indx: SEXP,
675            call: SEXP,
676        ) -> SEXP {
677            <StateType>::extract_subset(
678                Robj::from_sexp(x),
679                Robj::from_sexp(indx),
680                Robj::from_sexp(call),
681            )
682            .get()
683        }
684
685        unsafe {
686            let csname = std::ffi::CString::new(name).unwrap();
687            let csbase = std::ffi::CString::new(base).unwrap();
688
689            let class_ptr = match ty {
690                Rtype::Integers => {
691                    R_make_altinteger_class(csname.as_ptr(), csbase.as_ptr(), std::ptr::null_mut())
692                }
693                Rtype::Doubles => {
694                    R_make_altreal_class(csname.as_ptr(), csbase.as_ptr(), std::ptr::null_mut())
695                }
696                Rtype::Logicals => {
697                    R_make_altlogical_class(csname.as_ptr(), csbase.as_ptr(), std::ptr::null_mut())
698                }
699                Rtype::Raw => {
700                    R_make_altraw_class(csname.as_ptr(), csbase.as_ptr(), std::ptr::null_mut())
701                }
702                Rtype::Complexes => {
703                    R_make_altcomplex_class(csname.as_ptr(), csbase.as_ptr(), std::ptr::null_mut())
704                }
705                Rtype::Strings => {
706                    R_make_altstring_class(csname.as_ptr(), csbase.as_ptr(), std::ptr::null_mut())
707                }
708                #[cfg(use_r_altlist)]
709                Rtype::List => {
710                    R_make_altlist_class(csname.as_ptr(), csbase.as_ptr(), std::ptr::null_mut())
711                }
712                _ => panic!("expected Altvec compatible type"),
713            };
714
715            #[cfg(feature = "non-api")]
716            R_set_altrep_UnserializeEX_method(class_ptr, Some(altrep_UnserializeEX::<StateType>));
717            R_set_altrep_Unserialize_method(class_ptr, Some(altrep_Unserialize::<StateType>));
718            R_set_altrep_Serialized_state_method(
719                class_ptr,
720                Some(altrep_Serialized_state::<StateType>),
721            );
722            R_set_altrep_DuplicateEX_method(class_ptr, Some(altrep_DuplicateEX::<StateType>));
723            R_set_altrep_Duplicate_method(class_ptr, Some(altrep_Duplicate::<StateType>));
724            R_set_altrep_Coerce_method(class_ptr, Some(altrep_Coerce::<StateType>));
725            R_set_altrep_Inspect_method(class_ptr, Some(altrep_Inspect::<StateType>));
726            R_set_altrep_Length_method(class_ptr, Some(altrep_Length::<StateType>));
727
728            R_set_altvec_Dataptr_method(class_ptr, Some(altvec_Dataptr::<StateType>));
729            R_set_altvec_Dataptr_or_null_method(
730                class_ptr,
731                Some(altvec_Dataptr_or_null::<StateType>),
732            );
733            R_set_altvec_Extract_subset_method(class_ptr, Some(altvec_Extract_subset::<StateType>));
734
735            Robj::from_sexp(class_ptr.ptr)
736        }
737    }
738
739    /// Make an integer ALTREP class that can be used to make vectors.
740    pub fn make_altinteger_class<StateType: AltrepImpl + AltIntegerImpl + 'static>(
741        name: &str,
742        base: &str,
743    ) -> Robj {
744        #![allow(non_snake_case)]
745        use std::os::raw::c_int;
746
747        single_threaded(|| unsafe {
748            let class = Altrep::altrep_class::<StateType>(Rtype::Integers, name, base);
749            let class_ptr = R_altrep_class_t { ptr: class.get() };
750
751            unsafe extern "C" fn altinteger_Elt<StateType: AltIntegerImpl + 'static>(
752                x: SEXP,
753                i: R_xlen_t,
754            ) -> c_int {
755                Altrep::get_state::<StateType>(x).elt(i as usize).inner() as c_int
756            }
757
758            unsafe extern "C" fn altinteger_Get_region<StateType: AltIntegerImpl + 'static>(
759                x: SEXP,
760                i: R_xlen_t,
761                n: R_xlen_t,
762                buf: *mut c_int,
763            ) -> R_xlen_t {
764                let slice = std::slice::from_raw_parts_mut(buf as *mut Rint, n as usize);
765                Altrep::get_state::<StateType>(x).get_region(i as usize, slice) as R_xlen_t
766            }
767
768            unsafe extern "C" fn altinteger_Is_sorted<StateType: AltIntegerImpl + 'static>(
769                x: SEXP,
770            ) -> c_int {
771                Altrep::get_state::<StateType>(x).is_sorted().inner() as c_int
772            }
773
774            unsafe extern "C" fn altinteger_No_NA<StateType: AltIntegerImpl + 'static>(
775                x: SEXP,
776            ) -> c_int {
777                i32::from(Altrep::get_state::<StateType>(x).no_na())
778            }
779
780            unsafe extern "C" fn altinteger_Sum<StateType: AltIntegerImpl + 'static>(
781                x: SEXP,
782                narm: Rboolean,
783            ) -> SEXP {
784                Altrep::get_state::<StateType>(x)
785                    .sum(narm == Rboolean::TRUE)
786                    .get()
787            }
788
789            unsafe extern "C" fn altinteger_Min<StateType: AltIntegerImpl + 'static>(
790                x: SEXP,
791                narm: Rboolean,
792            ) -> SEXP {
793                Altrep::get_state::<StateType>(x)
794                    .min(narm == Rboolean::TRUE)
795                    .get()
796            }
797
798            unsafe extern "C" fn altinteger_Max<StateType: AltIntegerImpl + 'static>(
799                x: SEXP,
800                narm: Rboolean,
801            ) -> SEXP {
802                Altrep::get_state::<StateType>(x)
803                    .max(narm == Rboolean::TRUE)
804                    .get()
805            }
806
807            R_set_altinteger_Elt_method(class_ptr, Some(altinteger_Elt::<StateType>));
808            R_set_altinteger_Get_region_method(class_ptr, Some(altinteger_Get_region::<StateType>));
809            R_set_altinteger_Is_sorted_method(class_ptr, Some(altinteger_Is_sorted::<StateType>));
810            R_set_altinteger_No_NA_method(class_ptr, Some(altinteger_No_NA::<StateType>));
811            R_set_altinteger_Sum_method(class_ptr, Some(altinteger_Sum::<StateType>));
812            R_set_altinteger_Min_method(class_ptr, Some(altinteger_Min::<StateType>));
813            R_set_altinteger_Max_method(class_ptr, Some(altinteger_Max::<StateType>));
814
815            class
816        })
817    }
818
819    /// Make a real ALTREP class that can be used to make vectors.
820    pub fn make_altreal_class<StateType: AltrepImpl + AltRealImpl + 'static>(
821        name: &str,
822        base: &str,
823    ) -> Robj {
824        #![allow(non_snake_case)]
825        use std::os::raw::c_int;
826
827        single_threaded(|| unsafe {
828            let class = Altrep::altrep_class::<StateType>(Rtype::Doubles, name, base);
829            let class_ptr = R_altrep_class_t { ptr: class.get() };
830
831            unsafe extern "C" fn altreal_Elt<StateType: AltRealImpl + 'static>(
832                x: SEXP,
833                i: R_xlen_t,
834            ) -> f64 {
835                Altrep::get_state::<StateType>(x).elt(i as usize).inner()
836            }
837
838            unsafe extern "C" fn altreal_Get_region<StateType: AltRealImpl + 'static>(
839                x: SEXP,
840                i: R_xlen_t,
841                n: R_xlen_t,
842                buf: *mut f64,
843            ) -> R_xlen_t {
844                let slice = std::slice::from_raw_parts_mut(buf as *mut Rfloat, n as usize);
845                Altrep::get_state::<StateType>(x).get_region(i as usize, slice) as R_xlen_t
846            }
847
848            unsafe extern "C" fn altreal_Is_sorted<StateType: AltRealImpl + 'static>(
849                x: SEXP,
850            ) -> c_int {
851                Altrep::get_state::<StateType>(x).is_sorted().inner() as c_int
852            }
853
854            unsafe extern "C" fn altreal_No_NA<StateType: AltRealImpl + 'static>(x: SEXP) -> c_int {
855                i32::from(Altrep::get_state::<StateType>(x).no_na())
856            }
857
858            unsafe extern "C" fn altreal_Sum<StateType: AltRealImpl + 'static>(
859                x: SEXP,
860                narm: Rboolean,
861            ) -> SEXP {
862                Altrep::get_state::<StateType>(x)
863                    .sum(narm == Rboolean::TRUE)
864                    .get()
865            }
866
867            unsafe extern "C" fn altreal_Min<StateType: AltRealImpl + 'static>(
868                x: SEXP,
869                narm: Rboolean,
870            ) -> SEXP {
871                Altrep::get_state::<StateType>(x)
872                    .min(narm == Rboolean::TRUE)
873                    .get()
874            }
875
876            unsafe extern "C" fn altreal_Max<StateType: AltRealImpl + 'static>(
877                x: SEXP,
878                narm: Rboolean,
879            ) -> SEXP {
880                Altrep::get_state::<StateType>(x)
881                    .max(narm == Rboolean::TRUE)
882                    .get()
883            }
884
885            R_set_altreal_Elt_method(class_ptr, Some(altreal_Elt::<StateType>));
886            R_set_altreal_Get_region_method(class_ptr, Some(altreal_Get_region::<StateType>));
887            R_set_altreal_Is_sorted_method(class_ptr, Some(altreal_Is_sorted::<StateType>));
888            R_set_altreal_No_NA_method(class_ptr, Some(altreal_No_NA::<StateType>));
889            R_set_altreal_Sum_method(class_ptr, Some(altreal_Sum::<StateType>));
890            R_set_altreal_Min_method(class_ptr, Some(altreal_Min::<StateType>));
891            R_set_altreal_Max_method(class_ptr, Some(altreal_Max::<StateType>));
892            class
893        })
894    }
895
896    /// Make a logical ALTREP class that can be used to make vectors.
897    pub fn make_altlogical_class<StateType: AltrepImpl + AltLogicalImpl + 'static>(
898        name: &str,
899        base: &str,
900    ) -> Robj {
901        #![allow(non_snake_case)]
902        use std::os::raw::c_int;
903
904        single_threaded(|| unsafe {
905            let class = Altrep::altrep_class::<StateType>(Rtype::Logicals, name, base);
906            let class_ptr = R_altrep_class_t { ptr: class.get() };
907
908            unsafe extern "C" fn altlogical_Elt<StateType: AltLogicalImpl + 'static>(
909                x: SEXP,
910                i: R_xlen_t,
911            ) -> c_int {
912                Altrep::get_state::<StateType>(x).elt(i as usize).inner() as c_int
913            }
914
915            unsafe extern "C" fn altlogical_Get_region<StateType: AltLogicalImpl + 'static>(
916                x: SEXP,
917                i: R_xlen_t,
918                n: R_xlen_t,
919                buf: *mut c_int,
920            ) -> R_xlen_t {
921                let slice = std::slice::from_raw_parts_mut(buf as *mut Rbool, n as usize);
922                Altrep::get_state::<StateType>(x).get_region(i as usize, slice) as R_xlen_t
923            }
924
925            unsafe extern "C" fn altlogical_Is_sorted<StateType: AltLogicalImpl + 'static>(
926                x: SEXP,
927            ) -> c_int {
928                Altrep::get_state::<StateType>(x).is_sorted().inner() as c_int
929            }
930
931            unsafe extern "C" fn altlogical_No_NA<StateType: AltLogicalImpl + 'static>(
932                x: SEXP,
933            ) -> c_int {
934                i32::from(Altrep::get_state::<StateType>(x).no_na())
935            }
936
937            unsafe extern "C" fn altlogical_Sum<StateType: AltLogicalImpl + 'static>(
938                x: SEXP,
939                narm: Rboolean,
940            ) -> SEXP {
941                Altrep::get_state::<StateType>(x)
942                    .sum(narm == Rboolean::TRUE)
943                    .get()
944            }
945
946            R_set_altlogical_Elt_method(class_ptr, Some(altlogical_Elt::<StateType>));
947            R_set_altlogical_Get_region_method(class_ptr, Some(altlogical_Get_region::<StateType>));
948            R_set_altlogical_Is_sorted_method(class_ptr, Some(altlogical_Is_sorted::<StateType>));
949            R_set_altlogical_No_NA_method(class_ptr, Some(altlogical_No_NA::<StateType>));
950            R_set_altlogical_Sum_method(class_ptr, Some(altlogical_Sum::<StateType>));
951
952            class
953        })
954    }
955
956    /// Make a raw ALTREP class that can be used to make vectors.
957    pub fn make_altraw_class<StateType: AltrepImpl + AltRawImpl + 'static>(
958        name: &str,
959        base: &str,
960    ) -> Robj {
961        #![allow(non_snake_case)]
962
963        single_threaded(|| unsafe {
964            let class = Altrep::altrep_class::<StateType>(Rtype::Raw, name, base);
965            let class_ptr = R_altrep_class_t { ptr: class.get() };
966
967            unsafe extern "C" fn altraw_Elt<StateType: AltRawImpl + 'static>(
968                x: SEXP,
969                i: R_xlen_t,
970            ) -> Rbyte {
971                Altrep::get_state::<StateType>(x).elt(i as usize) as Rbyte
972            }
973
974            unsafe extern "C" fn altraw_Get_region<StateType: AltRawImpl + 'static>(
975                x: SEXP,
976                i: R_xlen_t,
977                n: R_xlen_t,
978                buf: *mut u8,
979            ) -> R_xlen_t {
980                let slice = std::slice::from_raw_parts_mut(buf, n as usize);
981                Altrep::get_state::<StateType>(x).get_region(i as usize, slice) as R_xlen_t
982            }
983
984            R_set_altraw_Elt_method(class_ptr, Some(altraw_Elt::<StateType>));
985            R_set_altraw_Get_region_method(class_ptr, Some(altraw_Get_region::<StateType>));
986
987            class
988        })
989    }
990
991    /// Make a complex ALTREP class that can be used to make vectors.
992    pub fn make_altcomplex_class<StateType: AltrepImpl + AltComplexImpl + 'static>(
993        name: &str,
994        base: &str,
995    ) -> Robj {
996        #![allow(non_snake_case)]
997
998        single_threaded(|| unsafe {
999            let class = Altrep::altrep_class::<StateType>(Rtype::Complexes, name, base);
1000            let class_ptr = R_altrep_class_t { ptr: class.get() };
1001
1002            unsafe extern "C" fn altcomplex_Elt<StateType: AltComplexImpl + 'static>(
1003                x: SEXP,
1004                i: R_xlen_t,
1005            ) -> Rcomplex {
1006                std::mem::transmute(Altrep::get_state::<StateType>(x).elt(i as usize))
1007            }
1008
1009            unsafe extern "C" fn altcomplex_Get_region<StateType: AltComplexImpl + 'static>(
1010                x: SEXP,
1011                i: R_xlen_t,
1012                n: R_xlen_t,
1013                buf: *mut Rcomplex,
1014            ) -> R_xlen_t {
1015                let slice = std::slice::from_raw_parts_mut(buf as *mut Rcplx, n as usize);
1016                Altrep::get_state::<StateType>(x).get_region(i as usize, slice) as R_xlen_t
1017            }
1018
1019            R_set_altcomplex_Elt_method(class_ptr, Some(altcomplex_Elt::<StateType>));
1020            R_set_altcomplex_Get_region_method(class_ptr, Some(altcomplex_Get_region::<StateType>));
1021
1022            class
1023        })
1024    }
1025
1026    /// Make a string ALTREP class that can be used to make vectors.
1027    pub fn make_altstring_class<StateType: AltrepImpl + AltStringImpl + 'static>(
1028        name: &str,
1029        base: &str,
1030    ) -> Robj {
1031        #![allow(non_snake_case)]
1032        use std::os::raw::c_int;
1033
1034        single_threaded(|| unsafe {
1035            let class = Altrep::altrep_class::<StateType>(Rtype::Strings, name, base);
1036            let class_ptr = R_altrep_class_t { ptr: class.get() };
1037
1038            unsafe extern "C" fn altstring_Elt<StateType: AltStringImpl + 'static>(
1039                x: SEXP,
1040                i: R_xlen_t,
1041            ) -> SEXP {
1042                Altrep::get_state::<StateType>(x).elt(i as usize).get()
1043            }
1044
1045            unsafe extern "C" fn altstring_Set_elt<StateType: AltStringImpl + 'static>(
1046                x: SEXP,
1047                i: R_xlen_t,
1048                v: SEXP,
1049            ) {
1050                Altrep::get_state_mut::<StateType>(x)
1051                    .set_elt(i as usize, Robj::from_sexp(v).try_into().unwrap())
1052            }
1053
1054            unsafe extern "C" fn altstring_Is_sorted<StateType: AltStringImpl + 'static>(
1055                x: SEXP,
1056            ) -> c_int {
1057                Altrep::get_state::<StateType>(x).is_sorted().inner() as c_int
1058            }
1059
1060            unsafe extern "C" fn altstring_No_NA<StateType: AltStringImpl + 'static>(
1061                x: SEXP,
1062            ) -> c_int {
1063                i32::from(Altrep::get_state::<StateType>(x).no_na())
1064            }
1065
1066            R_set_altstring_Elt_method(class_ptr, Some(altstring_Elt::<StateType>));
1067            R_set_altstring_Set_elt_method(class_ptr, Some(altstring_Set_elt::<StateType>));
1068            R_set_altstring_Is_sorted_method(class_ptr, Some(altstring_Is_sorted::<StateType>));
1069            R_set_altstring_No_NA_method(class_ptr, Some(altstring_No_NA::<StateType>));
1070
1071            class
1072        })
1073    }
1074
1075    #[cfg(use_r_altlist)]
1076    pub fn make_altlist_class<StateType: AltrepImpl + AltListImpl + 'static>(
1077        name: &str,
1078        base: &str,
1079    ) -> Robj {
1080        #![allow(non_snake_case)]
1081
1082        single_threaded(|| unsafe {
1083            let class = Altrep::altrep_class::<StateType>(Rtype::List, name, base);
1084            let class_ptr = R_altrep_class_t { ptr: class.get() };
1085
1086            unsafe extern "C" fn altlist_Elt<StateType: AltListImpl + 'static>(
1087                x: SEXP,
1088                i: R_xlen_t,
1089            ) -> SEXP {
1090                Altrep::get_state::<StateType>(x).elt(i as usize).get()
1091            }
1092
1093            unsafe extern "C" fn altlist_Set_elt<StateType: AltListImpl + 'static>(
1094                x: SEXP,
1095                i: R_xlen_t,
1096                v: SEXP,
1097            ) {
1098                Altrep::get_state_mut::<StateType>(x).set_elt(i as usize, Robj::from_sexp(v))
1099            }
1100
1101            R_set_altlist_Elt_method(class_ptr, Some(altlist_Elt::<StateType>));
1102            R_set_altlist_Set_elt_method(class_ptr, Some(altlist_Set_elt::<StateType>));
1103            class
1104        })
1105    }
1106
1107    make_from_iterator!(
1108        make_altinteger_from_iterator,
1109        make_altinteger_class,
1110        AltIntegerImpl,
1111        Rint,
1112        i32
1113    );
1114    make_from_iterator!(
1115        make_altlogical_from_iterator,
1116        make_altlogical_class,
1117        AltLogicalImpl,
1118        Rbool,
1119        i32
1120    );
1121    make_from_iterator!(
1122        make_altreal_from_iterator,
1123        make_altreal_class,
1124        AltRealImpl,
1125        Rfloat,
1126        f64
1127    );
1128    make_from_iterator!(
1129        make_altcomplex_from_iterator,
1130        make_altcomplex_class,
1131        AltComplexImpl,
1132        Rcplx,
1133        c64
1134    );
1135}
1136
1137impl<Iter: ExactSizeIterator + std::fmt::Debug + Clone> AltrepImpl for Iter {
1138    fn length(&self) -> usize {
1139        self.len()
1140    }
1141}