Skip to main content

miniextendr_api/altrep_impl/
arrays.rs

1//! ALTREP impls for const-generic arrays `[T; N]`.
2//!
3//! Arrays use const generics (`impl<const N: usize>`), so they can't ride the
4//! `impl_alt*_from_data!` macro path (which uses `$ty:ty` and needs a concrete
5//! type to delegate through). Numeric families share enough structure to be
6//! covered by the local `impl_altrep_array_numeric!` helper macro defined
7//! below; bool and String arrays differ structurally and are hand-written.
8
9// region: Array implementations (const generics)
10//
11// Macro-generated for numeric families (i32, f64, u8, Rcomplex) that share
12// the same pattern: Altrep + AltVec with dataptr + family trait + InferBase.
13// Bool and String arrays are hand-written because they differ structurally
14// (bool has no direct dataptr; String elt returns SEXP).
15
16/// Generate all ALTREP trait impls + InferBase for a numeric [T; N] array family.
17/// Pass optional extra items via `extra { ... }` to include in the family trait impl.
18macro_rules! impl_altrep_array_numeric {
19    (
20        elem = $elem:ty,
21        data_trait = $data_trait:path,
22        alt_trait = $alt_trait:path,
23        rbase = $rbase:expr,
24        make_class_fn = $make_class_fn:path,
25        install_family_fn = $install_family_fn:ident
26        $(, extra { $($extra:tt)* } )?
27        $(,)?
28    ) => {
29        impl<const N: usize> crate::altrep_traits::Altrep for [$elem; N] {
30            fn length(x: crate::SEXP) -> crate::R_xlen_t {
31                let data = unsafe {
32                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
33                };
34                crate::altrep_data::AltrepLen::len(data) as crate::R_xlen_t
35            }
36        }
37
38        impl<const N: usize> crate::altrep_traits::AltVec for [$elem; N] {
39            const HAS_DATAPTR: bool = true;
40
41            fn dataptr(x: crate::SEXP, _writable: bool) -> *mut core::ffi::c_void {
42                let data = unsafe {
43                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
44                };
45                data.as_mut_ptr().cast::<core::ffi::c_void>()
46            }
47        }
48
49        impl<const N: usize> $alt_trait for [$elem; N] {
50            const HAS_ELT: bool = true;
51
52            fn elt(x: crate::SEXP, i: crate::R_xlen_t) -> $elem {
53                let data = unsafe {
54                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
55                };
56                <[$elem; N] as $data_trait>::elt(data, i.max(0) as usize)
57            }
58
59            const HAS_GET_REGION: bool = true;
60
61            fn get_region(
62                x: crate::SEXP,
63                start: crate::R_xlen_t,
64                len: crate::R_xlen_t,
65                buf: &mut [$elem],
66            ) -> crate::R_xlen_t {
67                if start < 0 || len <= 0 {
68                    return 0;
69                }
70                let data = unsafe {
71                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
72                };
73                <[$elem; N] as $data_trait>::get_region(data, start as usize, len as usize, buf)
74                    as crate::R_xlen_t
75            }
76
77            $($($extra)*)?
78        }
79
80        impl<const N: usize> crate::altrep_data::InferBase for [$elem; N] {
81            const BASE: crate::altrep::RBase = $rbase;
82
83            unsafe fn make_class(
84                class_name: *const i8,
85                pkg_name: *const i8,
86            ) -> crate::sys::altrep::R_altrep_class_t {
87                let cls = unsafe { $make_class_fn(class_name, pkg_name, crate::altrep_dll_info()) };
88                let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
89                crate::altrep::validate_altrep_class(cls, name, Self::BASE)
90            }
91
92            unsafe fn install_methods(cls: crate::sys::altrep::R_altrep_class_t) {
93                unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
94                unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
95                unsafe { crate::altrep_bridge::$install_family_fn::<Self>(cls) };
96            }
97        }
98    };
99}
100
101/// no_na fragment for families that support it (Integer, Real).
102macro_rules! altrep_array_no_na {
103    ($elem:ty, $data_trait:path) => {
104        const HAS_NO_NA: bool = true;
105
106        fn no_na(x: crate::SEXP) -> i32 {
107            let data =
108                unsafe { <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
109            <[$elem; N] as $data_trait>::no_na(data)
110                .map(i32::from)
111                .unwrap_or(0)
112        }
113    };
114}
115
116impl_altrep_array_numeric!(
117    elem = i32,
118    data_trait = crate::altrep_data::AltIntegerData,
119    alt_trait = crate::altrep_traits::AltInteger,
120    rbase = crate::altrep::RBase::Int,
121    make_class_fn = crate::sys::altrep::R_make_altinteger_class,
122    install_family_fn = install_int,
123    extra { altrep_array_no_na!(i32, crate::altrep_data::AltIntegerData); },
124);
125impl_altrep_array_numeric!(
126    elem = f64,
127    data_trait = crate::altrep_data::AltRealData,
128    alt_trait = crate::altrep_traits::AltReal,
129    rbase = crate::altrep::RBase::Real,
130    make_class_fn = crate::sys::altrep::R_make_altreal_class,
131    install_family_fn = install_real,
132    extra { altrep_array_no_na!(f64, crate::altrep_data::AltRealData); },
133);
134impl_altrep_array_numeric!(
135    elem = u8,
136    data_trait = crate::altrep_data::AltRawData,
137    alt_trait = crate::altrep_traits::AltRaw,
138    rbase = crate::altrep::RBase::Raw,
139    make_class_fn = crate::sys::altrep::R_make_altraw_class,
140    install_family_fn = install_raw,
141);
142impl_altrep_array_numeric!(
143    elem = crate::Rcomplex,
144    data_trait = crate::altrep_data::AltComplexData,
145    alt_trait = crate::altrep_traits::AltComplex,
146    rbase = crate::altrep::RBase::Complex,
147    make_class_fn = crate::sys::altrep::R_make_altcomplex_class,
148    install_family_fn = install_cplx,
149);
150
151// Logical arrays — bool != i32, no direct dataptr, elt returns i32 via to_r_int()
152impl<const N: usize> crate::altrep_traits::Altrep for [bool; N] {
153    fn length(x: crate::SEXP) -> crate::R_xlen_t {
154        let data =
155            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
156        crate::altrep_data::AltrepLen::len(data) as crate::R_xlen_t
157    }
158}
159
160impl<const N: usize> crate::altrep_traits::AltVec for [bool; N] {}
161
162impl<const N: usize> crate::altrep_traits::AltLogical for [bool; N] {
163    const HAS_ELT: bool = true;
164
165    fn elt(x: crate::SEXP, i: crate::R_xlen_t) -> i32 {
166        let data =
167            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
168        <[bool; N] as crate::altrep_data::AltLogicalData>::elt(data, i.max(0) as usize).to_r_int()
169    }
170
171    const HAS_NO_NA: bool = true;
172
173    fn no_na(x: crate::SEXP) -> i32 {
174        let data =
175            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
176        <[bool; N] as crate::altrep_data::AltLogicalData>::no_na(data)
177            .map(i32::from)
178            .unwrap_or(0)
179    }
180}
181
182impl<const N: usize> crate::altrep_data::InferBase for [bool; N] {
183    const BASE: crate::altrep::RBase = crate::altrep::RBase::Logical;
184
185    unsafe fn make_class(
186        class_name: *const i8,
187        pkg_name: *const i8,
188    ) -> crate::sys::altrep::R_altrep_class_t {
189        let cls = unsafe {
190            crate::sys::altrep::R_make_altlogical_class(
191                class_name,
192                pkg_name,
193                crate::altrep_dll_info(),
194            )
195        };
196        let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
197        crate::altrep::validate_altrep_class(cls, name, Self::BASE)
198    }
199
200    unsafe fn install_methods(cls: crate::sys::altrep::R_altrep_class_t) {
201        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
202        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
203        unsafe { crate::altrep_bridge::install_lgl::<Self>(cls) };
204    }
205}
206
207// String arrays — no dataptr, elt returns SEXP via checked_mkchar
208impl<const N: usize> crate::altrep_traits::Altrep for [String; N] {
209    const GUARD: crate::altrep_traits::AltrepGuard = crate::altrep_traits::AltrepGuard::RUnwind;
210
211    fn length(x: crate::SEXP) -> crate::R_xlen_t {
212        let data =
213            unsafe { <[String; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
214        crate::altrep_data::AltrepLen::len(data) as crate::R_xlen_t
215    }
216}
217
218impl<const N: usize> crate::altrep_traits::AltVec for [String; N] {}
219
220impl<const N: usize> crate::altrep_traits::AltString for [String; N] {
221    fn elt(x: crate::SEXP, i: crate::R_xlen_t) -> crate::SEXP {
222        let data =
223            unsafe { <[String; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
224        match <[String; N] as crate::altrep_data::AltStringData>::elt(data, i.max(0) as usize) {
225            Some(s) => unsafe { super::checked_mkchar(s) },
226            None => crate::SEXP::na_string(),
227        }
228    }
229}
230
231impl<const N: usize> crate::altrep_data::InferBase for [String; N] {
232    const BASE: crate::altrep::RBase = crate::altrep::RBase::String;
233
234    unsafe fn make_class(
235        class_name: *const i8,
236        pkg_name: *const i8,
237    ) -> crate::sys::altrep::R_altrep_class_t {
238        let cls = unsafe {
239            crate::sys::altrep::R_make_altstring_class(
240                class_name,
241                pkg_name,
242                crate::altrep_dll_info(),
243            )
244        };
245        let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
246        crate::altrep::validate_altrep_class(cls, name, Self::BASE)
247    }
248
249    unsafe fn install_methods(cls: crate::sys::altrep::R_altrep_class_t) {
250        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
251        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
252        unsafe { crate::altrep_bridge::install_str::<Self>(cls) };
253    }
254}
255// endregion