Skip to main content

miniextendr_api/
altrep_impl.rs

1//! ALTREP implementation utilities.
2//!
3//! This module provides helper functions for implementing ALTREP classes.
4//! The proc-macro uses these to generate trait implementations.
5//!
6//! Use `crate::altrep_data1_as` (re-exported from externalptr) to extract
7//! data from an ALTREP's data1 slot.
8
9// region: Checked string-to-CHARSXP helper
10
11/// Create a CHARSXP from a Rust string, with checked length conversion.
12///
13/// # Safety
14///
15/// Must be called from R's main thread.
16///
17/// # Panics
18///
19/// Panics if `s.len() > i32::MAX`.
20#[inline]
21pub unsafe fn checked_mkchar(s: &str) -> crate::ffi::SEXP {
22    let _len = i32::try_from(s.len()).unwrap_or_else(|_| {
23        panic!(
24            "string length {} exceeds i32::MAX for Rf_mkCharLenCE",
25            s.len()
26        )
27    });
28    crate::ffi::SEXP::charsxp(s)
29}
30// endregion
31
32// region: Centralized ALTREP buffer access helper
33
34/// Create a mutable slice from an ALTREP `get_region` output buffer pointer.
35///
36/// Called by the bridge trampolines (`altrep_bridge.rs`) to convert the raw
37/// `*mut T` buffer from R's ALTREP dispatch into a `&mut [T]` before passing
38/// it to the trait methods.
39///
40/// # Safety
41///
42/// - `buf` must be a valid, aligned, writable pointer to at least `len` elements of `T`.
43/// - The caller must ensure no aliasing references to the same memory exist.
44/// - This is guaranteed when called from R's ALTREP `Get_region` dispatch, which
45///   provides a freshly allocated buffer.
46#[inline]
47pub unsafe fn altrep_region_buf<T>(buf: *mut T, len: usize) -> &'static mut [T] {
48    unsafe { std::slice::from_raw_parts_mut(buf, len) }
49}
50// endregion
51
52// region: Macros for generating trait implementations
53
54/// Generate ALTREP trait implementations for a type that implements AltIntegerData.
55///
56/// This macro generates `impl Altrep`, `impl AltVec`, and `impl AltInteger` for the type,
57/// delegating to the high-level `AltIntegerData` trait methods.
58///
59/// **Requires**: The type must implement `TypedExternal` (use `#[derive(ExternalPtr)]`).
60///
61/// ## Variants
62///
63/// ```ignore
64/// // Basic (no dataptr, no serialization):
65/// impl_altinteger_from_data!(MyType);
66///
67/// // With dataptr (type must implement AltrepDataptr<i32>):
68/// impl_altinteger_from_data!(MyType, dataptr);
69///
70/// // With serialization (type must implement AltrepSerialize):
71/// impl_altinteger_from_data!(MyType, serialize);
72///
73/// // With subset optimization (type must implement AltrepExtractSubset):
74/// impl_altinteger_from_data!(MyType, subset);
75///
76/// // Combine multiple options:
77/// impl_altinteger_from_data!(MyType, dataptr, serialize);
78/// impl_altinteger_from_data!(MyType, subset, serialize);
79/// ```
80#[macro_export]
81macro_rules! impl_altinteger_from_data {
82    ($ty:ty) => {
83        // Default: materializing DATAPTR — allocates INTSXP in data2 on first DATAPTR call.
84        // Without this, R's default DATAPTR errors with "cannot access data pointer".
85        $crate::__impl_altrep_base!($ty);
86        $crate::__impl_altvec_integer_dataptr!($ty);
87        $crate::__impl_altinteger_methods!($ty);
88        $crate::impl_inferbase_integer!($ty);
89    };
90    ($ty:ty, dataptr) => {
91        $crate::__impl_alt_from_data!(
92            $ty,
93            __impl_altinteger_methods,
94            impl_inferbase_integer,
95            dataptr(i32)
96        );
97    };
98    ($ty:ty, serialize) => {
99        // Serialize + materializing DATAPTR (default for computed types)
100        $crate::__impl_altrep_base_with_serialize!($ty);
101        $crate::__impl_altvec_integer_dataptr!($ty);
102        $crate::__impl_altinteger_methods!($ty);
103        $crate::impl_inferbase_integer!($ty);
104    };
105    ($ty:ty, subset) => {
106        $crate::__impl_alt_from_data!(
107            $ty,
108            __impl_altinteger_methods,
109            impl_inferbase_integer,
110            subset
111        );
112    };
113    ($ty:ty, dataptr, serialize) => {
114        $crate::__impl_alt_from_data!(
115            $ty,
116            __impl_altinteger_methods,
117            impl_inferbase_integer,
118            dataptr(i32),
119            serialize
120        );
121    };
122    ($ty:ty, serialize, dataptr) => {
123        $crate::impl_altinteger_from_data!($ty, dataptr, serialize);
124    };
125    ($ty:ty, subset, serialize) => {
126        $crate::__impl_alt_from_data!(
127            $ty,
128            __impl_altinteger_methods,
129            impl_inferbase_integer,
130            subset,
131            serialize
132        );
133    };
134    ($ty:ty, serialize, subset) => {
135        $crate::impl_altinteger_from_data!($ty, subset, serialize);
136    };
137    // Materializing dataptr only (no serialization)
138    ($ty:ty, materializing_dataptr) => {
139        $crate::__impl_altrep_base!($ty);
140        $crate::__impl_altvec_integer_dataptr!($ty);
141        $crate::__impl_altinteger_methods!($ty);
142        $crate::impl_inferbase_integer!($ty);
143    };
144    // Materializing dataptr + serialize (for computed types like Range<i32>)
145    ($ty:ty, materializing_dataptr, serialize) => {
146        $crate::__impl_altrep_base_with_serialize!($ty);
147        $crate::__impl_altvec_integer_dataptr!($ty);
148        $crate::__impl_altinteger_methods!($ty);
149        $crate::impl_inferbase_integer!($ty);
150    };
151}
152
153/// Internal macro: impl Altrep with just length
154#[macro_export]
155#[doc(hidden)]
156macro_rules! __impl_altrep_base {
157    ($ty:ty) => {
158        $crate::__impl_altrep_base!($ty, RUnwind);
159    };
160    ($ty:ty, $guard:ident) => {
161        impl $crate::altrep_traits::Altrep for $ty {
162            const GUARD: $crate::altrep_traits::AltrepGuard =
163                $crate::altrep_traits::AltrepGuard::$guard;
164
165            fn length(x: $crate::ffi::SEXP) -> $crate::ffi::R_xlen_t {
166                let data =
167                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
168                <$ty as $crate::altrep_data::AltrepLen>::len(data) as $crate::ffi::R_xlen_t
169            }
170        }
171    };
172}
173
174/// Internal macro: impl Altrep with length + serialization
175///
176/// This implements both:
177/// - `serialized_state(x)` (save-side)
178/// - `unserialize(class, state)` (load-side)
179///
180/// The `unserialize` implementation reconstructs the backing Rust value via
181/// [`AltrepSerialize::unserialize`] and then creates a fresh ALTREP instance via
182/// `R_new_altrep(class, data1, SEXP::nil())` where `data1` is an `ExternalPtr<$ty>`.
183///
184/// This matches the proc-macro-generated `IntoR::into_sexp` behavior (data is stored in `data1`,
185/// and `data2` is `R_NilValue`).
186#[macro_export]
187#[doc(hidden)]
188macro_rules! __impl_altrep_base_with_serialize {
189    ($ty:ty) => {
190        $crate::__impl_altrep_base_with_serialize!($ty, RUnwind);
191    };
192    ($ty:ty, $guard:ident) => {
193        impl $crate::altrep_traits::Altrep for $ty {
194            const GUARD: $crate::altrep_traits::AltrepGuard =
195                $crate::altrep_traits::AltrepGuard::$guard;
196
197            fn length(x: $crate::ffi::SEXP) -> $crate::ffi::R_xlen_t {
198                let data =
199                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
200                <$ty as $crate::altrep_data::AltrepLen>::len(data) as $crate::ffi::R_xlen_t
201            }
202
203            const HAS_SERIALIZED_STATE: bool = true;
204
205            fn serialized_state(x: $crate::ffi::SEXP) -> $crate::ffi::SEXP {
206                let data =
207                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
208                <$ty as $crate::altrep_data::AltrepSerialize>::serialized_state(data)
209            }
210
211            const HAS_UNSERIALIZE: bool = true;
212
213            fn unserialize(
214                class: $crate::ffi::SEXP,
215                state: $crate::ffi::SEXP,
216            ) -> $crate::ffi::SEXP {
217                let Some(data) = <$ty as $crate::altrep_data::AltrepSerialize>::unserialize(state)
218                else {
219                    panic!(
220                        "ALTREP unserialize failed for {}",
221                        core::any::type_name::<$ty>()
222                    );
223                };
224
225                // SAFETY: Unserialize is called by R on the main thread.
226                unsafe {
227                    use $crate::externalptr::ExternalPtr;
228                    use $crate::ffi::altrep::R_altrep_class_t;
229                    use $crate::ffi::{Rf_protect_unchecked, Rf_unprotect_unchecked, SEXP};
230
231                    let ext_ptr = ExternalPtr::new_unchecked(data);
232                    let data1 = ext_ptr.as_sexp();
233                    // Protect across the allocation in new_altrep_unchecked.
234                    Rf_protect_unchecked(data1);
235                    let cls = R_altrep_class_t::from_sexp(class);
236                    let out = cls.new_altrep_unchecked(data1, SEXP::nil());
237                    Rf_unprotect_unchecked(1);
238                    out
239                }
240            }
241        }
242    };
243}
244
245/// Internal macro: impl AltVec with dataptr support
246///
247/// When `writable = true`, obtains a mutable reference to the data via
248/// `altrep_data1_mut` so that writes through the returned pointer modify
249/// the Rust-owned data directly. When `writable = false`, uses the
250/// immutable `altrep_data1_as` + `dataptr_or_null` path, avoiding
251/// unnecessary mutable borrows (and, for `Cow`, avoiding a copy-on-write).
252#[macro_export]
253#[doc(hidden)]
254macro_rules! __impl_altvec_dataptr {
255    ($ty:ty, $elem:ty) => {
256        impl $crate::altrep_traits::AltVec for $ty {
257            const HAS_DATAPTR: bool = true;
258
259            fn dataptr(x: $crate::ffi::SEXP, writable: bool) -> *mut core::ffi::c_void {
260                // Check data2 cache first (materialized by a prior call).
261                unsafe {
262                    let data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2(&x);
263                    if !data2.is_null()
264                        && $crate::ffi::SexpExt::type_of(&data2)
265                            == <$elem as $crate::ffi::RNativeType>::SEXP_TYPE
266                    {
267                        return $crate::ffi::DATAPTR_RO(data2).cast_mut();
268                    }
269                }
270
271                // Try the fast path: direct pointer from the underlying data.
272                let direct = if writable {
273                    let d = unsafe {
274                        <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
275                    };
276                    <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr(d, true)
277                        .map(|p| p.cast::<core::ffi::c_void>())
278                } else {
279                    // Read-only: try immutable access first to avoid &mut borrows
280                    // and unnecessary copy-on-write for Cow types.
281                    let d = unsafe {
282                        <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
283                    };
284                    let ro = <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr_or_null(d);
285                    if let Some(p) = ro {
286                        return p.cast_mut().cast::<core::ffi::c_void>();
287                    }
288                    // dataptr_or_null returned None — try mutable path.
289                    let d = unsafe {
290                        <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
291                    };
292                    <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr(d, false)
293                        .map(|p| p.cast::<core::ffi::c_void>())
294                };
295
296                if let Some(p) = direct {
297                    return p;
298                }
299
300                // The underlying data can't provide a contiguous pointer (e.g., Arrow
301                // array with null bitmask). Materialize into data2 via Elt methods.
302                // Must never return null — R doesn't fall back when custom Dataptr is set.
303                unsafe { $crate::altrep_data::materialize_altrep_data2::<$elem>(x) }
304            }
305
306            const HAS_DATAPTR_OR_NULL: bool = true;
307
308            fn dataptr_or_null(x: $crate::ffi::SEXP) -> *const core::ffi::c_void {
309                // Check data2 cache first (may have been materialized by a prior dataptr call)
310                unsafe {
311                    let data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2(&x);
312                    if !data2.is_null()
313                        && $crate::ffi::SexpExt::type_of(&data2)
314                            == <$elem as $crate::ffi::RNativeType>::SEXP_TYPE
315                    {
316                        return $crate::ffi::DATAPTR_RO(data2);
317                    }
318                }
319                let d =
320                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
321                <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr_or_null(d)
322                    .map(|p| p.cast::<core::ffi::c_void>())
323                    .unwrap_or(core::ptr::null())
324            }
325        }
326    };
327}
328
329/// Internal macro: impl AltVec with dataptr support for string ALTREP.
330///
331/// String vectors (STRSXP) store CHARSXP pointers, not contiguous data. This macro
332/// materializes remaining uncached elements into the data2 STRSXP cache (which may
333/// already have some elements from prior `Elt` calls). Returns the cached STRSXP's
334/// data pointer.
335#[macro_export]
336#[doc(hidden)]
337macro_rules! __impl_altvec_string_dataptr {
338    ($ty:ty) => {
339        impl $crate::altrep_traits::AltVec for $ty {
340            const HAS_DATAPTR: bool = true;
341
342            fn dataptr(x: $crate::ffi::SEXP, _writable: bool) -> *mut core::ffi::c_void {
343                unsafe {
344                    let n = <$ty as $crate::altrep_traits::Altrep>::length(x);
345
346                    // Get or allocate the data2 cache STRSXP
347                    let mut data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2(&x);
348                    let fresh_alloc = data2.is_null()
349                        || $crate::ffi::SexpExt::type_of(&data2) != $crate::ffi::SEXPTYPE::STRSXP;
350                    if fresh_alloc {
351                        // Rf_allocVector(STRSXP, n) leaves elements UNINITIALIZED
352                        // (garbage SEXP pointers). Must fill with R_NaString sentinel
353                        // so cache lookups work. This is O(n) but unavoidable.
354                        data2 = $crate::ffi::Rf_protect($crate::ffi::Rf_allocVector(
355                            $crate::ffi::SEXPTYPE::STRSXP,
356                            n,
357                        ));
358                        for j in 0..n {
359                            $crate::ffi::SexpExt::set_string_elt(
360                                &data2,
361                                j,
362                                $crate::ffi::SEXP::na_string(),
363                            );
364                        }
365                        $crate::altrep_ext::AltrepSexpExt::set_altrep_data2(&x, data2);
366                        $crate::ffi::Rf_unprotect(1);
367                    }
368
369                    // Fill uncached elements only — elements already cached by Elt
370                    // are non-NA CHARSXPs and are skipped. NA elements are re-probed
371                    // from Rust (O(1)) to handle mixed cached/uncached NA slots.
372                    for i in 0..n {
373                        let cached = $crate::ffi::SexpExt::string_elt(&data2, i);
374                        if cached != $crate::ffi::SEXP::na_string() {
375                            continue; // already cached by a prior Elt call
376                        }
377                        // Compute from Rust and store
378                        let elt = <$ty as $crate::altrep_traits::AltString>::elt(x, i);
379                        $crate::ffi::SexpExt::set_string_elt(&data2, i, elt);
380                    }
381
382                    $crate::ffi::DATAPTR_RO(data2).cast_mut()
383                }
384            }
385
386            const HAS_DATAPTR_OR_NULL: bool = true;
387
388            fn dataptr_or_null(x: $crate::ffi::SEXP) -> *const core::ffi::c_void {
389                // Always return null. The data2 STRSXP may be partially cached
390                // (Elt filled some slots, others are R_NaString sentinels).
391                // Returning a pointer to a partial cache would expose sentinel
392                // R_NaString as actual NAs. Returning null tells R to use
393                // Elt-based access, which correctly handles the per-element cache.
394                // Dataptr (not dataptr_or_null) is the full-materialization path.
395                let _ = x;
396                core::ptr::null()
397            }
398        }
399    };
400}
401
402/// Internal macro: impl AltVec with materializing dataptr for logical ALTREP.
403///
404/// Thin wrapper: provides a trivial `AltrepDataptr` (no direct pointer) and
405/// delegates to `__impl_altvec_dataptr` which materializes via `RNativeType::elt`.
406/// R logicals are stored as `i32` but accessed through `RLogical` for type safety.
407#[macro_export]
408#[doc(hidden)]
409macro_rules! __impl_altvec_logical_dataptr {
410    ($ty:ty) => {
411        impl $crate::altrep_data::AltrepDataptr<$crate::ffi::RLogical> for $ty {
412            fn dataptr(&mut self, _writable: bool) -> Option<*mut $crate::ffi::RLogical> {
413                None
414            }
415        }
416        $crate::__impl_altvec_dataptr!($ty, $crate::ffi::RLogical);
417    };
418}
419
420/// Internal macro: impl AltVec with materializing dataptr for integer ALTREP.
421///
422/// Internal macro: impl AltVec with materializing dataptr for integer ALTREP.
423///
424/// Thin wrapper: provides a trivial `AltrepDataptr` (no direct pointer) and
425/// delegates to `__impl_altvec_dataptr` which materializes via `RNativeType::elt`.
426#[macro_export]
427#[doc(hidden)]
428macro_rules! __impl_altvec_integer_dataptr {
429    ($ty:ty) => {
430        impl $crate::altrep_data::AltrepDataptr<i32> for $ty {
431            fn dataptr(&mut self, _writable: bool) -> Option<*mut i32> {
432                None
433            }
434        }
435        $crate::__impl_altvec_dataptr!($ty, i32);
436    };
437}
438
439/// Internal macro: impl AltVec with materializing dataptr for real ALTREP.
440#[macro_export]
441#[doc(hidden)]
442macro_rules! __impl_altvec_real_dataptr {
443    ($ty:ty) => {
444        impl $crate::altrep_data::AltrepDataptr<f64> for $ty {
445            fn dataptr(&mut self, _writable: bool) -> Option<*mut f64> {
446                None
447            }
448        }
449        $crate::__impl_altvec_dataptr!($ty, f64);
450    };
451}
452
453/// Internal macro: impl AltVec with materializing dataptr for raw ALTREP.
454#[macro_export]
455#[doc(hidden)]
456macro_rules! __impl_altvec_raw_dataptr {
457    ($ty:ty) => {
458        impl $crate::altrep_data::AltrepDataptr<u8> for $ty {
459            fn dataptr(&mut self, _writable: bool) -> Option<*mut u8> {
460                None
461            }
462        }
463        $crate::__impl_altvec_dataptr!($ty, u8);
464    };
465}
466
467/// Internal macro: impl AltVec with materializing dataptr for complex ALTREP.
468#[macro_export]
469#[doc(hidden)]
470macro_rules! __impl_altvec_complex_dataptr {
471    ($ty:ty) => {
472        impl $crate::altrep_data::AltrepDataptr<$crate::ffi::Rcomplex> for $ty {
473            fn dataptr(&mut self, _writable: bool) -> Option<*mut $crate::ffi::Rcomplex> {
474                None
475            }
476        }
477        $crate::__impl_altvec_dataptr!($ty, $crate::ffi::Rcomplex);
478    };
479}
480
481/// Internal macro: impl AltVec with extract_subset support
482#[macro_export]
483#[doc(hidden)]
484macro_rules! __impl_altvec_extract_subset {
485    ($ty:ty) => {
486        impl $crate::altrep_traits::AltVec for $ty {
487            const HAS_EXTRACT_SUBSET: bool = true;
488
489            fn extract_subset(
490                x: $crate::ffi::SEXP,
491                indx: $crate::ffi::SEXP,
492                _call: $crate::ffi::SEXP,
493            ) -> $crate::ffi::SEXP {
494                // Validate that indx is an integer vector before calling INTEGER().
495                // Return NULL to signal R to use default subsetting if not.
496                if $crate::ffi::SexpExt::type_of(&indx) != $crate::ffi::SEXPTYPE::INTSXP {
497                    return core::ptr::null_mut();
498                }
499
500                // Convert indx SEXP to slice using SexpExt (avoids raw-ptr-deref lint)
501                let indices = unsafe { $crate::ffi::SexpExt::as_slice::<i32>(&indx) };
502
503                let data =
504                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
505                <$ty as $crate::altrep_data::AltrepExtractSubset>::extract_subset(data, indices)
506                    .unwrap_or($crate::ffi::SEXP::nil())
507            }
508        }
509    };
510}
511// endregion
512
513// region: Shared building-block macros for ALTREP trait implementations
514//
515// These macros expand to associated items inside `impl` blocks. They are
516// invoked by the per-family `__impl_alt*_methods!` macros to eliminate
517// code duplication across the 7 ALTREP type families.
518
519/// Shared `elt` implementation for ALTREP families with direct element access.
520///
521/// Generates `const HAS_ELT` and `fn elt(...)` inside an impl block.
522/// Used by integer, real, raw, and complex families.
523#[macro_export]
524#[doc(hidden)]
525macro_rules! __impl_alt_elt {
526    ($ty:ty, $trait:path, $elem:ty, $na:expr) => {
527        const HAS_ELT: bool = true;
528
529        fn elt(x: $crate::ffi::SEXP, i: $crate::ffi::R_xlen_t) -> $elem {
530            let data =
531                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
532            <$ty as $trait>::elt(data, i.max(0) as usize)
533        }
534    };
535}
536
537/// Shared `get_region` implementation for ALTREP families.
538///
539/// Generates `const HAS_GET_REGION` and `fn get_region(...)` inside an impl block.
540/// Used by integer, real, logical, raw, and complex families.
541#[macro_export]
542#[doc(hidden)]
543macro_rules! __impl_alt_get_region {
544    ($ty:ty, $trait:path, $buf_ty:ty) => {
545        const HAS_GET_REGION: bool = true;
546
547        fn get_region(
548            x: $crate::ffi::SEXP,
549            start: $crate::ffi::R_xlen_t,
550            len: $crate::ffi::R_xlen_t,
551            buf: &mut [$buf_ty],
552        ) -> $crate::ffi::R_xlen_t {
553            if start < 0 || len <= 0 {
554                return 0;
555            }
556            let data =
557                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
558            let len = len as usize;
559            <$ty as $trait>::get_region(data, start as usize, len, buf) as $crate::ffi::R_xlen_t
560        }
561    };
562}
563
564/// Shared `is_sorted` implementation for ALTREP families.
565///
566/// Generates `const HAS_IS_SORTED` and `fn is_sorted(...)` inside an impl block.
567/// Used by integer, real, logical, and string families.
568#[macro_export]
569#[doc(hidden)]
570macro_rules! __impl_alt_is_sorted {
571    ($ty:ty, $trait:path) => {
572        const HAS_IS_SORTED: bool = true;
573
574        fn is_sorted(x: $crate::ffi::SEXP) -> i32 {
575            let data =
576                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
577            <$ty as $trait>::is_sorted(data)
578                .map(|s| s.to_r_int())
579                .unwrap_or(i32::MIN)
580        }
581    };
582}
583
584/// Shared `no_na` implementation for ALTREP families.
585///
586/// Generates `const HAS_NO_NA` and `fn no_na(...)` inside an impl block.
587/// Used by integer, real, logical, and string families.
588#[macro_export]
589#[doc(hidden)]
590macro_rules! __impl_alt_no_na {
591    ($ty:ty, $trait:path) => {
592        const HAS_NO_NA: bool = true;
593
594        fn no_na(x: $crate::ffi::SEXP) -> i32 {
595            let data =
596                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
597            <$ty as $trait>::no_na(data)
598                .map(|b| if b { 1 } else { 0 })
599                .unwrap_or(0)
600        }
601    };
602}
603// endregion
604
605// region: Parametric macro: __impl_alt_from_data!
606//
607// This internal macro generates the standard ALTREP trait implementations
608// (Altrep, AltVec, family-specific methods, InferBase) for a given type.
609// The 7 public `impl_alt*_from_data!` macros delegate to this with
610// family-specific parameters.
611
612#[macro_export]
613#[doc(hidden)]
614macro_rules! __impl_alt_from_data {
615    // Base: no options
616    ($ty:ty, $methods:ident, $inferbase:ident) => {
617        $crate::__impl_altrep_base!($ty);
618        impl $crate::altrep_traits::AltVec for $ty {}
619        $crate::$methods!($ty);
620        $crate::$inferbase!($ty);
621    };
622    // Base: explicit guard
623    ($ty:ty, $methods:ident, $inferbase:ident, @guard $guard:ident) => {
624        $crate::__impl_altrep_base!($ty, $guard);
625        impl $crate::altrep_traits::AltVec for $ty {}
626        $crate::$methods!($ty);
627        $crate::$inferbase!($ty);
628    };
629    // Dataptr with element type
630    ($ty:ty, $methods:ident, $inferbase:ident, dataptr($elem:ty)) => {
631        $crate::__impl_altrep_base!($ty);
632        $crate::__impl_altvec_dataptr!($ty, $elem);
633        $crate::$methods!($ty);
634        $crate::$inferbase!($ty);
635    };
636    // String dataptr (materialization into STRSXP)
637    ($ty:ty, $methods:ident, $inferbase:ident, string_dataptr) => {
638        $crate::__impl_altrep_base!($ty);
639        $crate::__impl_altvec_string_dataptr!($ty);
640        $crate::$methods!($ty);
641        $crate::$inferbase!($ty);
642    };
643    // String dataptr + explicit guard
644    ($ty:ty, $methods:ident, $inferbase:ident, string_dataptr, @guard $guard:ident) => {
645        $crate::__impl_altrep_base!($ty, $guard);
646        $crate::__impl_altvec_string_dataptr!($ty);
647        $crate::$methods!($ty);
648        $crate::$inferbase!($ty);
649    };
650    // Serialize only
651    ($ty:ty, $methods:ident, $inferbase:ident, serialize) => {
652        $crate::__impl_altrep_base_with_serialize!($ty);
653        impl $crate::altrep_traits::AltVec for $ty {}
654        $crate::$methods!($ty);
655        $crate::$inferbase!($ty);
656    };
657    // Serialize + explicit guard
658    ($ty:ty, $methods:ident, $inferbase:ident, serialize, @guard $guard:ident) => {
659        $crate::__impl_altrep_base_with_serialize!($ty, $guard);
660        impl $crate::altrep_traits::AltVec for $ty {}
661        $crate::$methods!($ty);
662        $crate::$inferbase!($ty);
663    };
664    // Subset only
665    ($ty:ty, $methods:ident, $inferbase:ident, subset) => {
666        $crate::__impl_altrep_base!($ty);
667        $crate::__impl_altvec_extract_subset!($ty);
668        $crate::$methods!($ty);
669        $crate::$inferbase!($ty);
670    };
671    // Dataptr + serialize
672    ($ty:ty, $methods:ident, $inferbase:ident, dataptr($elem:ty), serialize) => {
673        $crate::__impl_altrep_base_with_serialize!($ty);
674        $crate::__impl_altvec_dataptr!($ty, $elem);
675        $crate::$methods!($ty);
676        $crate::$inferbase!($ty);
677    };
678    // String dataptr + serialize
679    ($ty:ty, $methods:ident, $inferbase:ident, string_dataptr, serialize) => {
680        $crate::__impl_altrep_base_with_serialize!($ty);
681        $crate::__impl_altvec_string_dataptr!($ty);
682        $crate::$methods!($ty);
683        $crate::$inferbase!($ty);
684    };
685    // String dataptr + serialize + explicit guard
686    ($ty:ty, $methods:ident, $inferbase:ident, string_dataptr, serialize, @guard $guard:ident) => {
687        $crate::__impl_altrep_base_with_serialize!($ty, $guard);
688        $crate::__impl_altvec_string_dataptr!($ty);
689        $crate::$methods!($ty);
690        $crate::$inferbase!($ty);
691    };
692    // Subset + serialize
693    ($ty:ty, $methods:ident, $inferbase:ident, subset, serialize) => {
694        $crate::__impl_altrep_base_with_serialize!($ty);
695        $crate::__impl_altvec_extract_subset!($ty);
696        $crate::$methods!($ty);
697        $crate::$inferbase!($ty);
698    };
699}
700// endregion
701
702// region: Per-family method macros (using shared building blocks)
703
704/// Internal macro for AltInteger method implementations.
705#[macro_export]
706#[doc(hidden)]
707macro_rules! __impl_altinteger_methods {
708    ($ty:ty) => {
709        impl $crate::altrep_traits::AltInteger for $ty {
710            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltIntegerData, i32, i32::MIN);
711            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltIntegerData, i32);
712            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltIntegerData);
713            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltIntegerData);
714
715            const HAS_SUM: bool = true;
716
717            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
718            fn sum(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
719                let data =
720                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
721                match <$ty as $crate::altrep_data::AltIntegerData>::sum(data, narm) {
722                    Some(s) => {
723                        if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
724                            $crate::ffi::SEXP::scalar_integer(s as i32)
725                        } else {
726                            $crate::ffi::SEXP::scalar_real(s as f64)
727                        }
728                    }
729                    None => $crate::ffi::SEXP::null(),
730                }
731            }
732
733            const HAS_MIN: bool = true;
734
735            fn min(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
736                let data =
737                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
738                <$ty as $crate::altrep_data::AltIntegerData>::min(data, narm)
739                    .map(|m| $crate::ffi::SEXP::scalar_integer(m))
740                    .unwrap_or($crate::ffi::SEXP::null())
741            }
742
743            const HAS_MAX: bool = true;
744
745            fn max(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
746                let data =
747                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
748                <$ty as $crate::altrep_data::AltIntegerData>::max(data, narm)
749                    .map(|m| $crate::ffi::SEXP::scalar_integer(m))
750                    .unwrap_or($crate::ffi::SEXP::null())
751            }
752        }
753    };
754}
755
756/// Generate ALTREP trait implementations for a type that implements AltRealData.
757///
758/// ## Variants
759///
760/// ```ignore
761/// // Basic (no dataptr, no serialization):
762/// impl_altreal_from_data!(MyType);
763///
764/// // With dataptr (type must implement AltrepDataptr<f64>):
765/// impl_altreal_from_data!(MyType, dataptr);
766///
767/// // With serialization (type must implement AltrepSerialize):
768/// impl_altreal_from_data!(MyType, serialize);
769///
770/// // With subset optimization (type must implement AltrepExtractSubset):
771/// impl_altreal_from_data!(MyType, subset);
772///
773/// // Combine multiple options:
774/// impl_altreal_from_data!(MyType, dataptr, serialize);
775/// impl_altreal_from_data!(MyType, subset, serialize);
776/// ```
777#[macro_export]
778macro_rules! impl_altreal_from_data {
779    ($ty:ty) => {
780        $crate::__impl_altrep_base!($ty);
781        $crate::__impl_altvec_real_dataptr!($ty);
782        $crate::__impl_altreal_methods!($ty);
783        $crate::impl_inferbase_real!($ty);
784    };
785    ($ty:ty, dataptr) => {
786        $crate::__impl_alt_from_data!(
787            $ty,
788            __impl_altreal_methods,
789            impl_inferbase_real,
790            dataptr(f64)
791        );
792    };
793    ($ty:ty, serialize) => {
794        $crate::__impl_altrep_base_with_serialize!($ty);
795        $crate::__impl_altvec_real_dataptr!($ty);
796        $crate::__impl_altreal_methods!($ty);
797        $crate::impl_inferbase_real!($ty);
798    };
799    ($ty:ty, dataptr, serialize) => {
800        $crate::__impl_alt_from_data!(
801            $ty,
802            __impl_altreal_methods,
803            impl_inferbase_real,
804            dataptr(f64),
805            serialize
806        );
807    };
808    ($ty:ty, serialize, dataptr) => {
809        $crate::impl_altreal_from_data!($ty, dataptr, serialize);
810    };
811    ($ty:ty, subset) => {
812        $crate::__impl_alt_from_data!($ty, __impl_altreal_methods, impl_inferbase_real, subset);
813    };
814    ($ty:ty, subset, serialize) => {
815        $crate::__impl_alt_from_data!(
816            $ty,
817            __impl_altreal_methods,
818            impl_inferbase_real,
819            subset,
820            serialize
821        );
822    };
823    ($ty:ty, serialize, subset) => {
824        $crate::impl_altreal_from_data!($ty, subset, serialize);
825    };
826    // Materializing dataptr only (no serialization)
827    ($ty:ty, materializing_dataptr) => {
828        $crate::__impl_altrep_base!($ty);
829        $crate::__impl_altvec_real_dataptr!($ty);
830        $crate::__impl_altreal_methods!($ty);
831        $crate::impl_inferbase_real!($ty);
832    };
833    // Materializing dataptr + serialize (for computed types like Range<f64>)
834    ($ty:ty, materializing_dataptr, serialize) => {
835        $crate::__impl_altrep_base_with_serialize!($ty);
836        $crate::__impl_altvec_real_dataptr!($ty);
837        $crate::__impl_altreal_methods!($ty);
838        $crate::impl_inferbase_real!($ty);
839    };
840}
841
842/// Internal macro for AltReal method implementations.
843#[macro_export]
844#[doc(hidden)]
845macro_rules! __impl_altreal_methods {
846    ($ty:ty) => {
847        impl $crate::altrep_traits::AltReal for $ty {
848            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltRealData, f64, f64::NAN);
849            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltRealData, f64);
850            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltRealData);
851            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltRealData);
852
853            const HAS_SUM: bool = true;
854
855            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
856            fn sum(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
857                let data =
858                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
859                <$ty as $crate::altrep_data::AltRealData>::sum(data, narm)
860                    .map(|s| $crate::ffi::SEXP::scalar_real(s))
861                    .unwrap_or($crate::ffi::SEXP::null())
862            }
863
864            const HAS_MIN: bool = true;
865
866            fn min(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
867                let data =
868                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
869                <$ty as $crate::altrep_data::AltRealData>::min(data, narm)
870                    .map(|m| $crate::ffi::SEXP::scalar_real(m))
871                    .unwrap_or($crate::ffi::SEXP::null())
872            }
873
874            const HAS_MAX: bool = true;
875
876            fn max(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
877                let data =
878                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
879                <$ty as $crate::altrep_data::AltRealData>::max(data, narm)
880                    .map(|m| $crate::ffi::SEXP::scalar_real(m))
881                    .unwrap_or($crate::ffi::SEXP::null())
882            }
883        }
884    };
885}
886
887/// Generate ALTREP trait implementations for a type that implements AltLogicalData.
888///
889/// ## Variants
890///
891/// ```ignore
892/// // Basic (no dataptr, no serialization):
893/// impl_altlogical_from_data!(MyType);
894///
895/// // With dataptr (type must implement AltrepDataptr<i32>):
896/// impl_altlogical_from_data!(MyType, dataptr);
897///
898/// // With serialization (type must implement AltrepSerialize):
899/// impl_altlogical_from_data!(MyType, serialize);
900///
901/// // With subset optimization (type must implement AltrepExtractSubset):
902/// impl_altlogical_from_data!(MyType, subset);
903///
904/// // Combine multiple options:
905/// impl_altlogical_from_data!(MyType, dataptr, serialize);
906/// impl_altlogical_from_data!(MyType, subset, serialize);
907/// ```
908#[macro_export]
909macro_rules! impl_altlogical_from_data {
910    ($ty:ty) => {
911        $crate::__impl_altrep_base!($ty);
912        $crate::__impl_altvec_logical_dataptr!($ty);
913        $crate::__impl_altlogical_methods!($ty);
914        $crate::impl_inferbase_logical!($ty);
915    };
916    ($ty:ty, dataptr) => {
917        $crate::__impl_alt_from_data!(
918            $ty,
919            __impl_altlogical_methods,
920            impl_inferbase_logical,
921            dataptr(i32)
922        );
923    };
924    ($ty:ty, serialize) => {
925        $crate::__impl_altrep_base_with_serialize!($ty);
926        $crate::__impl_altvec_logical_dataptr!($ty);
927        $crate::__impl_altlogical_methods!($ty);
928        $crate::impl_inferbase_logical!($ty);
929    };
930    ($ty:ty, dataptr, serialize) => {
931        $crate::__impl_alt_from_data!(
932            $ty,
933            __impl_altlogical_methods,
934            impl_inferbase_logical,
935            dataptr(i32),
936            serialize
937        );
938    };
939    ($ty:ty, serialize, dataptr) => {
940        $crate::impl_altlogical_from_data!($ty, dataptr, serialize);
941    };
942    ($ty:ty, subset) => {
943        $crate::__impl_alt_from_data!(
944            $ty,
945            __impl_altlogical_methods,
946            impl_inferbase_logical,
947            subset
948        );
949    };
950    ($ty:ty, subset, serialize) => {
951        $crate::__impl_alt_from_data!(
952            $ty,
953            __impl_altlogical_methods,
954            impl_inferbase_logical,
955            subset,
956            serialize
957        );
958    };
959    ($ty:ty, serialize, subset) => {
960        $crate::impl_altlogical_from_data!($ty, subset, serialize);
961    };
962    // Materializing dataptr only (no serialization)
963    ($ty:ty, materializing_dataptr) => {
964        $crate::__impl_altrep_base!($ty);
965        $crate::__impl_altvec_logical_dataptr!($ty);
966        $crate::__impl_altlogical_methods!($ty);
967        $crate::impl_inferbase_logical!($ty);
968    };
969    // Materializing dataptr + serialize (for bool types that need bool→i32 conversion)
970    ($ty:ty, materializing_dataptr, serialize) => {
971        $crate::__impl_altrep_base_with_serialize!($ty);
972        $crate::__impl_altvec_logical_dataptr!($ty);
973        $crate::__impl_altlogical_methods!($ty);
974        $crate::impl_inferbase_logical!($ty);
975    };
976}
977
978/// Internal macro: impl AltLogical methods from AltLogicalData
979#[macro_export]
980#[doc(hidden)]
981macro_rules! __impl_altlogical_methods {
982    ($ty:ty) => {
983        impl $crate::altrep_traits::AltLogical for $ty {
984            // Logical elt is special: returns Logical → .to_r_int()
985            const HAS_ELT: bool = true;
986
987            fn elt(x: $crate::ffi::SEXP, i: $crate::ffi::R_xlen_t) -> i32 {
988                let data =
989                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
990                <$ty as $crate::altrep_data::AltLogicalData>::elt(data, i.max(0) as usize)
991                    .to_r_int()
992            }
993
994            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltLogicalData, i32);
995            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltLogicalData);
996            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltLogicalData);
997
998            const HAS_SUM: bool = true;
999
1000            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
1001            fn sum(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
1002                let data =
1003                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1004                match <$ty as $crate::altrep_data::AltLogicalData>::sum(data, narm) {
1005                    Some(s) => {
1006                        if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
1007                            $crate::ffi::SEXP::scalar_integer(s as i32)
1008                        } else {
1009                            $crate::ffi::SEXP::scalar_real(s as f64)
1010                        }
1011                    }
1012                    None => $crate::ffi::SEXP::null(),
1013                }
1014            }
1015        }
1016    };
1017}
1018
1019/// Generate ALTREP trait implementations for a type that implements AltRawData.
1020///
1021/// ## Variants
1022///
1023/// ```ignore
1024/// // Basic (no dataptr, no serialization):
1025/// impl_altraw_from_data!(MyType);
1026///
1027/// // With dataptr (type must implement AltrepDataptr<u8>):
1028/// impl_altraw_from_data!(MyType, dataptr);
1029///
1030/// // With serialization (type must implement AltrepSerialize):
1031/// impl_altraw_from_data!(MyType, serialize);
1032///
1033/// // With subset optimization (type must implement AltrepExtractSubset):
1034/// impl_altraw_from_data!(MyType, subset);
1035///
1036/// // Combine multiple options:
1037/// impl_altraw_from_data!(MyType, dataptr, serialize);
1038/// impl_altraw_from_data!(MyType, subset, serialize);
1039/// ```
1040#[macro_export]
1041macro_rules! impl_altraw_from_data {
1042    ($ty:ty) => {
1043        $crate::__impl_altrep_base!($ty);
1044        $crate::__impl_altvec_raw_dataptr!($ty);
1045        $crate::__impl_altraw_methods!($ty);
1046        $crate::impl_inferbase_raw!($ty);
1047    };
1048    ($ty:ty, dataptr) => {
1049        $crate::__impl_alt_from_data!($ty, __impl_altraw_methods, impl_inferbase_raw, dataptr(u8));
1050    };
1051    ($ty:ty, serialize) => {
1052        $crate::__impl_altrep_base_with_serialize!($ty);
1053        $crate::__impl_altvec_raw_dataptr!($ty);
1054        $crate::__impl_altraw_methods!($ty);
1055        $crate::impl_inferbase_raw!($ty);
1056    };
1057    ($ty:ty, dataptr, serialize) => {
1058        $crate::__impl_alt_from_data!(
1059            $ty,
1060            __impl_altraw_methods,
1061            impl_inferbase_raw,
1062            dataptr(u8),
1063            serialize
1064        );
1065    };
1066    ($ty:ty, serialize, dataptr) => {
1067        $crate::impl_altraw_from_data!($ty, dataptr, serialize);
1068    };
1069    ($ty:ty, subset) => {
1070        $crate::__impl_alt_from_data!($ty, __impl_altraw_methods, impl_inferbase_raw, subset);
1071    };
1072    ($ty:ty, subset, serialize) => {
1073        $crate::__impl_alt_from_data!(
1074            $ty,
1075            __impl_altraw_methods,
1076            impl_inferbase_raw,
1077            subset,
1078            serialize
1079        );
1080    };
1081    ($ty:ty, serialize, subset) => {
1082        $crate::impl_altraw_from_data!($ty, subset, serialize);
1083    };
1084}
1085
1086/// Internal macro for AltRaw method implementations.
1087#[macro_export]
1088#[doc(hidden)]
1089macro_rules! __impl_altraw_methods {
1090    ($ty:ty) => {
1091        impl $crate::altrep_traits::AltRaw for $ty {
1092            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltRawData, u8, 0);
1093            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltRawData, u8);
1094        }
1095    };
1096}
1097
1098/// Generate ALTREP trait implementations for a type that implements AltStringData.
1099///
1100/// ## Variants
1101///
1102/// ```ignore
1103/// // Basic (no serialization):
1104/// impl_altstring_from_data!(MyType);
1105///
1106/// // With dataptr (materialized STRSXP):
1107/// impl_altstring_from_data!(MyType, dataptr);
1108///
1109/// // With serialization (type must implement AltrepSerialize):
1110/// impl_altstring_from_data!(MyType, serialize);
1111///
1112/// // With subset optimization (type must implement AltrepExtractSubset):
1113/// impl_altstring_from_data!(MyType, subset);
1114///
1115/// // Combine multiple options:
1116/// impl_altstring_from_data!(MyType, dataptr, serialize);
1117/// impl_altstring_from_data!(MyType, subset, serialize);
1118/// ```
1119#[macro_export]
1120macro_rules! impl_altstring_from_data {
1121    ($ty:ty) => {
1122        $crate::__impl_altrep_base!($ty);
1123        $crate::__impl_altvec_string_dataptr!($ty);
1124        $crate::__impl_altstring_methods!($ty);
1125        $crate::impl_inferbase_string!($ty);
1126    };
1127    ($ty:ty, dataptr) => {
1128        $crate::__impl_alt_from_data!(
1129            $ty,
1130            __impl_altstring_methods,
1131            impl_inferbase_string,
1132            string_dataptr
1133        );
1134    };
1135    ($ty:ty, serialize) => {
1136        $crate::__impl_altrep_base_with_serialize!($ty);
1137        $crate::__impl_altvec_string_dataptr!($ty);
1138        $crate::__impl_altstring_methods!($ty);
1139        $crate::impl_inferbase_string!($ty);
1140    };
1141    ($ty:ty, dataptr, serialize) => {
1142        $crate::__impl_alt_from_data!(
1143            $ty,
1144            __impl_altstring_methods,
1145            impl_inferbase_string,
1146            string_dataptr,
1147            serialize
1148        );
1149    };
1150    ($ty:ty, subset) => {
1151        $crate::__impl_alt_from_data!($ty, __impl_altstring_methods, impl_inferbase_string, subset);
1152    };
1153    ($ty:ty, subset, serialize) => {
1154        $crate::__impl_alt_from_data!(
1155            $ty,
1156            __impl_altstring_methods,
1157            impl_inferbase_string,
1158            subset,
1159            serialize
1160        );
1161    };
1162    ($ty:ty, serialize, subset) => {
1163        $crate::impl_altstring_from_data!($ty, subset, serialize);
1164    };
1165}
1166
1167/// Internal macro for AltString method implementations.
1168#[macro_export]
1169#[doc(hidden)]
1170macro_rules! __impl_altstring_methods {
1171    ($ty:ty) => {
1172        impl $crate::altrep_traits::AltString for $ty {
1173            // String elt with lazy per-element caching in data2 STRSXP.
1174            //
1175            // On first access, allocates a STRSXP in data2 (initialized to R_NaString).
1176            // Each element is computed from Rust on first access and cached. Subsequent
1177            // accesses return the cached CHARSXP directly.
1178            //
1179            // For NA elements (Rust elt returns None), data2[i] stays R_NaString — we
1180            // re-probe Rust each time (O(1) index, returns None immediately). This is
1181            // simpler than a separate materialization bitmap and the cost is negligible.
1182            fn elt(x: $crate::ffi::SEXP, i: $crate::ffi::R_xlen_t) -> $crate::ffi::SEXP {
1183                unsafe {
1184                    let idx = i.max(0) as usize;
1185
1186                    // Get or allocate the data2 cache STRSXP
1187                    let mut data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2(&x);
1188                    if data2.is_null()
1189                        || $crate::ffi::SexpExt::type_of(&data2) != $crate::ffi::SEXPTYPE::STRSXP
1190                    {
1191                        let n = <$ty as $crate::altrep_traits::Altrep>::length(x);
1192                        // Rf_allocVector(STRSXP, n) leaves elements UNINITIALIZED
1193                        // (garbage SEXP pointers). Must fill with R_NaString sentinel.
1194                        data2 = $crate::ffi::Rf_protect($crate::ffi::Rf_allocVector(
1195                            $crate::ffi::SEXPTYPE::STRSXP,
1196                            n,
1197                        ));
1198                        for j in 0..n {
1199                            $crate::ffi::SexpExt::set_string_elt(
1200                                &data2,
1201                                j,
1202                                $crate::ffi::SEXP::na_string(),
1203                            );
1204                        }
1205                        $crate::altrep_ext::AltrepSexpExt::set_altrep_data2(&x, data2);
1206                        $crate::ffi::Rf_unprotect(1);
1207                    }
1208
1209                    // Check cache: non-NA means already materialized
1210                    let cached = $crate::ffi::SexpExt::string_elt(&data2, i);
1211                    if cached != $crate::ffi::SEXP::na_string() {
1212                        return cached;
1213                    }
1214
1215                    // Cache miss (or genuine NA) — probe Rust source
1216                    let data = <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x);
1217                    match <$ty as $crate::altrep_data::AltStringData>::elt(data, idx) {
1218                        Some(s) => {
1219                            let charsxp = $crate::altrep_impl::checked_mkchar(s);
1220                            $crate::ffi::SexpExt::set_string_elt(&data2, i, charsxp);
1221                            charsxp
1222                        }
1223                        None => $crate::ffi::SEXP::na_string(),
1224                    }
1225                }
1226            }
1227
1228            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltStringData);
1229            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltStringData);
1230        }
1231    };
1232}
1233
1234/// Generate ALTREP trait implementations for a type that implements AltListData.
1235#[macro_export]
1236macro_rules! impl_altlist_from_data {
1237    ($ty:ty) => {
1238        $crate::impl_altlist_from_data!($ty, RUnwind);
1239    };
1240    ($ty:ty, $guard:ident) => {
1241        impl $crate::altrep_traits::Altrep for $ty {
1242            const GUARD: $crate::altrep_traits::AltrepGuard =
1243                $crate::altrep_traits::AltrepGuard::$guard;
1244
1245            fn length(x: $crate::ffi::SEXP) -> $crate::ffi::R_xlen_t {
1246                let data =
1247                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1248                <$ty as $crate::altrep_data::AltrepLen>::len(data) as $crate::ffi::R_xlen_t
1249            }
1250        }
1251
1252        impl $crate::altrep_traits::AltVec for $ty {}
1253
1254        impl $crate::altrep_traits::AltList for $ty {
1255            fn elt(x: $crate::ffi::SEXP, i: $crate::ffi::R_xlen_t) -> $crate::ffi::SEXP {
1256                let data =
1257                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1258                <$ty as $crate::altrep_data::AltListData>::elt(data, i.max(0) as usize)
1259            }
1260        }
1261
1262        $crate::impl_inferbase_list!($ty);
1263    };
1264}
1265
1266/// Internal macro: impl AltComplex methods (elt, get_region)
1267#[macro_export]
1268#[doc(hidden)]
1269macro_rules! __impl_altcomplex_methods {
1270    ($ty:ty) => {
1271        impl $crate::altrep_traits::AltComplex for $ty {
1272            $crate::__impl_alt_elt!(
1273                $ty,
1274                $crate::altrep_data::AltComplexData,
1275                $crate::ffi::Rcomplex,
1276                $crate::ffi::Rcomplex {
1277                    r: f64::NAN,
1278                    i: f64::NAN
1279                }
1280            );
1281            $crate::__impl_alt_get_region!(
1282                $ty,
1283                $crate::altrep_data::AltComplexData,
1284                $crate::ffi::Rcomplex
1285            );
1286        }
1287    };
1288}
1289
1290/// Generate ALTREP trait implementations for a type that implements AltComplexData.
1291///
1292/// Optional features can be enabled by passing additional arguments:
1293/// - `dataptr`: Enable `Dataptr` and `Dataptr_or_null` methods (requires `AltrepDataptr<Rcomplex>`)
1294/// - `serialize`: Enable serialization support (requires `AltrepSerialize`)
1295/// - `subset`: Enable optimized subsetting (requires `AltrepExtractSubset`)
1296#[macro_export]
1297macro_rules! impl_altcomplex_from_data {
1298    ($ty:ty) => {
1299        $crate::__impl_altrep_base!($ty);
1300        $crate::__impl_altvec_complex_dataptr!($ty);
1301        $crate::__impl_altcomplex_methods!($ty);
1302        $crate::impl_inferbase_complex!($ty);
1303    };
1304    ($ty:ty, dataptr) => {
1305        $crate::__impl_alt_from_data!(
1306            $ty,
1307            __impl_altcomplex_methods,
1308            impl_inferbase_complex,
1309            dataptr($crate::ffi::Rcomplex)
1310        );
1311    };
1312    ($ty:ty, serialize) => {
1313        $crate::__impl_altrep_base_with_serialize!($ty);
1314        $crate::__impl_altvec_complex_dataptr!($ty);
1315        $crate::__impl_altcomplex_methods!($ty);
1316        $crate::impl_inferbase_complex!($ty);
1317    };
1318    ($ty:ty, subset) => {
1319        $crate::__impl_alt_from_data!(
1320            $ty,
1321            __impl_altcomplex_methods,
1322            impl_inferbase_complex,
1323            subset
1324        );
1325    };
1326    ($ty:ty, dataptr, serialize) => {
1327        $crate::__impl_alt_from_data!(
1328            $ty,
1329            __impl_altcomplex_methods,
1330            impl_inferbase_complex,
1331            dataptr($crate::ffi::Rcomplex),
1332            serialize
1333        );
1334    };
1335    ($ty:ty, serialize, dataptr) => {
1336        $crate::impl_altcomplex_from_data!($ty, dataptr, serialize);
1337    };
1338    ($ty:ty, subset, serialize) => {
1339        $crate::__impl_alt_from_data!(
1340            $ty,
1341            __impl_altcomplex_methods,
1342            impl_inferbase_complex,
1343            subset,
1344            serialize
1345        );
1346    };
1347    ($ty:ty, serialize, subset) => {
1348        $crate::impl_altcomplex_from_data!($ty, subset, serialize);
1349    };
1350}
1351// endregion
1352
1353// region: Built-in implementations for standard types
1354// These implementations are provided here to satisfy the orphan rules.
1355// User crates can use these types directly with delegate_data.
1356//
1357// All types that implement AltrepSerialize get the `serialize` option enabled,
1358// which allows proper saveRDS/readRDS round-trips.
1359
1360// Integer types - Vec<i32> supports direct dataptr, ranges materialize on demand
1361impl_altinteger_from_data!(Vec<i32>, dataptr, serialize);
1362impl_altinteger_from_data!(std::ops::Range<i32>, materializing_dataptr, serialize);
1363impl_altinteger_from_data!(std::ops::Range<i64>, materializing_dataptr, serialize);
1364
1365// Real types - Vec<f64> supports direct dataptr, ranges materialize on demand
1366impl_altreal_from_data!(Vec<f64>, dataptr, serialize);
1367impl_altreal_from_data!(std::ops::Range<f64>, materializing_dataptr, serialize);
1368
1369// Logical types — materializing dataptr converts bool→i32 into cached LGLSXP
1370impl_altlogical_from_data!(Vec<bool>, materializing_dataptr, serialize);
1371
1372// Raw types - u8 == Rbyte, supports direct dataptr
1373impl_altraw_from_data!(Vec<u8>, dataptr, serialize);
1374
1375// String types - Vec<String> supports dataptr via materialization into STRSXP
1376impl_altstring_from_data!(Vec<String>, dataptr, serialize);
1377// Vec<Option<String>> preserves NA_character_ through serialization roundtrips
1378impl_altstring_from_data!(Vec<Option<String>>, dataptr, serialize);
1379// Cow string vectors — zero-copy from R, ALTREP output without copying back.
1380// Serialize: Rf_mkCharLenCE hits R's CHARSXP cache (no string data copy for borrowed).
1381// Unserialize: TryFromSexp uses charsxp_to_cow (zero-copy borrow for UTF-8).
1382impl_altstring_from_data!(Vec<std::borrow::Cow<'static, str>>, dataptr, serialize);
1383impl_altstring_from_data!(
1384    Vec<Option<std::borrow::Cow<'static, str>>>,
1385    dataptr,
1386    serialize
1387);
1388
1389// Complex types - Vec<Rcomplex> supports dataptr
1390impl_altcomplex_from_data!(Vec<crate::ffi::Rcomplex>, dataptr, serialize);
1391// endregion
1392
1393// region: Box<[T]> implementations
1394// Box<[T]> is a fat pointer (Sized) that wraps a DST slice.
1395// Unlike Vec<T>, it has no capacity field - just ptr + len (2 words).
1396// Useful for fixed-size heap allocations.
1397
1398impl_altinteger_from_data!(Box<[i32]>, dataptr, serialize);
1399impl_altreal_from_data!(Box<[f64]>, dataptr, serialize);
1400impl_altlogical_from_data!(Box<[bool]>, materializing_dataptr, serialize);
1401impl_altraw_from_data!(Box<[u8]>, dataptr, serialize);
1402impl_altstring_from_data!(Box<[String]>, dataptr, serialize);
1403impl_altcomplex_from_data!(Box<[crate::ffi::Rcomplex]>, dataptr, serialize);
1404
1405// Cow<'static, [T]> — zero-copy borrow from R with copy-on-write dataptr.
1406// Borrowed variants expose R's data directly; Owned behaves like Vec.
1407impl_altinteger_from_data!(std::borrow::Cow<'static, [i32]>, dataptr, serialize);
1408impl_altreal_from_data!(std::borrow::Cow<'static, [f64]>, dataptr, serialize);
1409impl_altraw_from_data!(std::borrow::Cow<'static, [u8]>, dataptr, serialize);
1410impl_altcomplex_from_data!(
1411    std::borrow::Cow<'static, [crate::ffi::Rcomplex]>,
1412    dataptr,
1413    serialize
1414);
1415
1416/// Eagerly register all built-in ALTREP classes.
1417///
1418/// Must be called during `R_init` so that R can find these classes when
1419/// unserializing (readRDS) in a fresh session. Without this, the lazy
1420/// `OnceLock` registration means classes don't exist until first use —
1421/// too late for readRDS.
1422pub(crate) fn register_builtin_altrep_classes() {
1423    use crate::altrep::RegisterAltrep;
1424
1425    // Vec<T>
1426    Vec::<i32>::get_or_init_class();
1427    Vec::<f64>::get_or_init_class();
1428    Vec::<bool>::get_or_init_class();
1429    Vec::<u8>::get_or_init_class();
1430    Vec::<String>::get_or_init_class();
1431    Vec::<Option<String>>::get_or_init_class();
1432    Vec::<crate::ffi::Rcomplex>::get_or_init_class();
1433
1434    // Cow string vectors
1435    Vec::<std::borrow::Cow<'static, str>>::get_or_init_class();
1436    Vec::<Option<std::borrow::Cow<'static, str>>>::get_or_init_class();
1437
1438    // Box<[T]>
1439    Box::<[i32]>::get_or_init_class();
1440    Box::<[f64]>::get_or_init_class();
1441    Box::<[bool]>::get_or_init_class();
1442    Box::<[u8]>::get_or_init_class();
1443    Box::<[String]>::get_or_init_class();
1444    Box::<[crate::ffi::Rcomplex]>::get_or_init_class();
1445
1446    // Cow<'static, [T]>
1447    std::borrow::Cow::<'static, [i32]>::get_or_init_class();
1448    std::borrow::Cow::<'static, [f64]>::get_or_init_class();
1449    std::borrow::Cow::<'static, [u8]>::get_or_init_class();
1450    std::borrow::Cow::<'static, [crate::ffi::Rcomplex]>::get_or_init_class();
1451
1452    // Range types
1453    std::ops::Range::<i32>::get_or_init_class();
1454    std::ops::Range::<i64>::get_or_init_class();
1455    std::ops::Range::<f64>::get_or_init_class();
1456}
1457
1458/// Register Arrow ALTREP classes (behind feature gate).
1459#[cfg(feature = "arrow")]
1460pub(crate) fn register_arrow_altrep_classes() {
1461    use crate::altrep::RegisterAltrep;
1462    use crate::optionals::arrow_impl::*;
1463
1464    Float64Array::get_or_init_class();
1465    Int32Array::get_or_init_class();
1466    UInt8Array::get_or_init_class();
1467    BooleanArray::get_or_init_class();
1468    StringArray::get_or_init_class();
1469}
1470
1471// endregion
1472
1473// region: Array implementations (const generics)
1474//
1475// Macro-generated for numeric families (i32, f64, u8, Rcomplex) that share
1476// the same pattern: Altrep + AltVec with dataptr + family trait + InferBase.
1477// Bool and String arrays are hand-written because they differ structurally
1478// (bool has no direct dataptr; String elt returns SEXP).
1479
1480/// Generate all ALTREP trait impls + InferBase for a numeric [T; N] array family.
1481/// Pass optional extra items via `extra { ... }` to include in the family trait impl.
1482macro_rules! impl_altrep_array_numeric {
1483    (
1484        elem = $elem:ty,
1485        data_trait = $data_trait:path,
1486        alt_trait = $alt_trait:path,
1487        rbase = $rbase:expr,
1488        make_class_fn = $make_class_fn:path,
1489        install_family_fn = $install_family_fn:ident
1490        $(, extra { $($extra:tt)* } )?
1491        $(,)?
1492    ) => {
1493        impl<const N: usize> crate::altrep_traits::Altrep for [$elem; N] {
1494            fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1495                let data = unsafe {
1496                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1497                };
1498                crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
1499            }
1500        }
1501
1502        impl<const N: usize> crate::altrep_traits::AltVec for [$elem; N] {
1503            const HAS_DATAPTR: bool = true;
1504
1505            fn dataptr(x: crate::ffi::SEXP, _writable: bool) -> *mut core::ffi::c_void {
1506                let data = unsafe {
1507                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
1508                };
1509                data.as_mut_ptr().cast::<core::ffi::c_void>()
1510            }
1511        }
1512
1513        impl<const N: usize> $alt_trait for [$elem; N] {
1514            const HAS_ELT: bool = true;
1515
1516            fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> $elem {
1517                let data = unsafe {
1518                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1519                };
1520                <[$elem; N] as $data_trait>::elt(data, i.max(0) as usize)
1521            }
1522
1523            const HAS_GET_REGION: bool = true;
1524
1525            fn get_region(
1526                x: crate::ffi::SEXP,
1527                start: crate::ffi::R_xlen_t,
1528                len: crate::ffi::R_xlen_t,
1529                buf: &mut [$elem],
1530            ) -> crate::ffi::R_xlen_t {
1531                if start < 0 || len <= 0 {
1532                    return 0;
1533                }
1534                let data = unsafe {
1535                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1536                };
1537                <[$elem; N] as $data_trait>::get_region(data, start as usize, len as usize, buf)
1538                    as crate::ffi::R_xlen_t
1539            }
1540
1541            $($($extra)*)?
1542        }
1543
1544        impl<const N: usize> crate::altrep_data::InferBase for [$elem; N] {
1545            const BASE: crate::altrep::RBase = $rbase;
1546
1547            unsafe fn make_class(
1548                class_name: *const i8,
1549                pkg_name: *const i8,
1550            ) -> crate::ffi::altrep::R_altrep_class_t {
1551                let cls = unsafe { $make_class_fn(class_name, pkg_name, crate::altrep_dll_info()) };
1552                let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
1553                crate::altrep::validate_altrep_class(cls, name, Self::BASE)
1554            }
1555
1556            unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
1557                unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
1558                unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
1559                unsafe { crate::altrep_bridge::$install_family_fn::<Self>(cls) };
1560            }
1561        }
1562    };
1563}
1564
1565/// no_na fragment for families that support it (Integer, Real).
1566macro_rules! altrep_array_no_na {
1567    ($elem:ty, $data_trait:path) => {
1568        const HAS_NO_NA: bool = true;
1569
1570        fn no_na(x: crate::ffi::SEXP) -> i32 {
1571            let data =
1572                unsafe { <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1573            <[$elem; N] as $data_trait>::no_na(data)
1574                .map(i32::from)
1575                .unwrap_or(0)
1576        }
1577    };
1578}
1579
1580impl_altrep_array_numeric!(
1581    elem = i32,
1582    data_trait = crate::altrep_data::AltIntegerData,
1583    alt_trait = crate::altrep_traits::AltInteger,
1584    rbase = crate::altrep::RBase::Int,
1585    make_class_fn = crate::ffi::altrep::R_make_altinteger_class,
1586    install_family_fn = install_int,
1587    extra { altrep_array_no_na!(i32, crate::altrep_data::AltIntegerData); },
1588);
1589impl_altrep_array_numeric!(
1590    elem = f64,
1591    data_trait = crate::altrep_data::AltRealData,
1592    alt_trait = crate::altrep_traits::AltReal,
1593    rbase = crate::altrep::RBase::Real,
1594    make_class_fn = crate::ffi::altrep::R_make_altreal_class,
1595    install_family_fn = install_real,
1596    extra { altrep_array_no_na!(f64, crate::altrep_data::AltRealData); },
1597);
1598impl_altrep_array_numeric!(
1599    elem = u8,
1600    data_trait = crate::altrep_data::AltRawData,
1601    alt_trait = crate::altrep_traits::AltRaw,
1602    rbase = crate::altrep::RBase::Raw,
1603    make_class_fn = crate::ffi::altrep::R_make_altraw_class,
1604    install_family_fn = install_raw,
1605);
1606impl_altrep_array_numeric!(
1607    elem = crate::ffi::Rcomplex,
1608    data_trait = crate::altrep_data::AltComplexData,
1609    alt_trait = crate::altrep_traits::AltComplex,
1610    rbase = crate::altrep::RBase::Complex,
1611    make_class_fn = crate::ffi::altrep::R_make_altcomplex_class,
1612    install_family_fn = install_cplx,
1613);
1614
1615// Logical arrays — bool != i32, no direct dataptr, elt returns i32 via to_r_int()
1616impl<const N: usize> crate::altrep_traits::Altrep for [bool; N] {
1617    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1618        let data =
1619            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1620        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
1621    }
1622}
1623
1624impl<const N: usize> crate::altrep_traits::AltVec for [bool; N] {}
1625
1626impl<const N: usize> crate::altrep_traits::AltLogical for [bool; N] {
1627    const HAS_ELT: bool = true;
1628
1629    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> i32 {
1630        let data =
1631            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1632        <[bool; N] as crate::altrep_data::AltLogicalData>::elt(data, i.max(0) as usize).to_r_int()
1633    }
1634
1635    const HAS_NO_NA: bool = true;
1636
1637    fn no_na(x: crate::ffi::SEXP) -> i32 {
1638        let data =
1639            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1640        <[bool; N] as crate::altrep_data::AltLogicalData>::no_na(data)
1641            .map(i32::from)
1642            .unwrap_or(0)
1643    }
1644}
1645
1646impl<const N: usize> crate::altrep_data::InferBase for [bool; N] {
1647    const BASE: crate::altrep::RBase = crate::altrep::RBase::Logical;
1648
1649    unsafe fn make_class(
1650        class_name: *const i8,
1651        pkg_name: *const i8,
1652    ) -> crate::ffi::altrep::R_altrep_class_t {
1653        let cls = unsafe {
1654            crate::ffi::altrep::R_make_altlogical_class(
1655                class_name,
1656                pkg_name,
1657                crate::altrep_dll_info(),
1658            )
1659        };
1660        let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
1661        crate::altrep::validate_altrep_class(cls, name, Self::BASE)
1662    }
1663
1664    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
1665        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
1666        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
1667        unsafe { crate::altrep_bridge::install_lgl::<Self>(cls) };
1668    }
1669}
1670
1671// String arrays — no dataptr, elt returns SEXP via checked_mkchar
1672impl<const N: usize> crate::altrep_traits::Altrep for [String; N] {
1673    const GUARD: crate::altrep_traits::AltrepGuard = crate::altrep_traits::AltrepGuard::RUnwind;
1674
1675    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1676        let data =
1677            unsafe { <[String; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1678        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
1679    }
1680}
1681
1682impl<const N: usize> crate::altrep_traits::AltVec for [String; N] {}
1683
1684impl<const N: usize> crate::altrep_traits::AltString for [String; N] {
1685    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::SEXP {
1686        let data =
1687            unsafe { <[String; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1688        match <[String; N] as crate::altrep_data::AltStringData>::elt(data, i.max(0) as usize) {
1689            Some(s) => unsafe { checked_mkchar(s) },
1690            None => crate::ffi::SEXP::na_string(),
1691        }
1692    }
1693}
1694
1695impl<const N: usize> crate::altrep_data::InferBase for [String; N] {
1696    const BASE: crate::altrep::RBase = crate::altrep::RBase::String;
1697
1698    unsafe fn make_class(
1699        class_name: *const i8,
1700        pkg_name: *const i8,
1701    ) -> crate::ffi::altrep::R_altrep_class_t {
1702        let cls = unsafe {
1703            crate::ffi::altrep::R_make_altstring_class(
1704                class_name,
1705                pkg_name,
1706                crate::altrep_dll_info(),
1707            )
1708        };
1709        let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
1710        crate::altrep::validate_altrep_class(cls, name, Self::BASE)
1711    }
1712
1713    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
1714        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
1715        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
1716        unsafe { crate::altrep_bridge::install_str::<Self>(cls) };
1717    }
1718}
1719// endregion
1720
1721// region: Static slice implementations (&'static [T])
1722//
1723// `&'static [T]` is Sized (fat pointer: ptr + len) and satisfies 'static,
1724// so it can be used DIRECTLY with ALTREP via ExternalPtr.
1725//
1726// Use cases:
1727// - Const arrays: `static DATA: [i32; 5] = [1, 2, 3, 4, 5]; create_altrep(&DATA[..])`
1728// - Leaked data: `let s: &'static [i32] = Box::leak(vec.into_boxed_slice());`
1729// - Memory-mapped files with 'static lifetime
1730
1731// Integer static slices
1732impl crate::altrep_traits::Altrep for &'static [i32] {
1733    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1734        let data =
1735            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1736        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
1737    }
1738}
1739
1740impl crate::altrep_traits::AltVec for &'static [i32] {
1741    const HAS_DATAPTR: bool = true;
1742
1743    fn dataptr(x: crate::ffi::SEXP, writable: bool) -> *mut std::ffi::c_void {
1744        // Static data cannot be modified. Panic is caught by RUnwind guard.
1745        assert!(
1746            !writable,
1747            "cannot get writable DATAPTR for static ALTREP data"
1748        );
1749        let data =
1750            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1751        data.as_ptr().cast::<std::ffi::c_void>().cast_mut()
1752    }
1753
1754    const HAS_DATAPTR_OR_NULL: bool = true;
1755
1756    fn dataptr_or_null(x: crate::ffi::SEXP) -> *const std::ffi::c_void {
1757        let data =
1758            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1759        data.as_ptr().cast::<std::ffi::c_void>()
1760    }
1761}
1762
1763impl crate::altrep_traits::AltInteger for &'static [i32] {
1764    const HAS_ELT: bool = true;
1765
1766    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> i32 {
1767        let data =
1768            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1769        crate::altrep_data::AltIntegerData::elt(data, i.max(0) as usize)
1770    }
1771
1772    const HAS_GET_REGION: bool = true;
1773
1774    fn get_region(
1775        x: crate::ffi::SEXP,
1776        start: crate::ffi::R_xlen_t,
1777        len: crate::ffi::R_xlen_t,
1778        buf: &mut [i32],
1779    ) -> crate::ffi::R_xlen_t {
1780        if start < 0 || len <= 0 {
1781            return 0;
1782        }
1783        let data =
1784            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1785        let len = len as usize;
1786        crate::altrep_data::AltIntegerData::get_region(data, start as usize, len, buf)
1787            as crate::ffi::R_xlen_t
1788    }
1789
1790    const HAS_NO_NA: bool = true;
1791
1792    fn no_na(x: crate::ffi::SEXP) -> i32 {
1793        let data =
1794            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1795        crate::altrep_data::AltIntegerData::no_na(data)
1796            .map(|b| if b { 1 } else { 0 })
1797            .unwrap_or(0)
1798    }
1799
1800    const HAS_SUM: bool = true;
1801
1802    // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
1803    fn sum(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
1804        let data =
1805            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1806        crate::altrep_data::AltIntegerData::sum(data, narm)
1807            .map(|s| {
1808                if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
1809                    crate::ffi::SEXP::scalar_integer(s as i32)
1810                } else {
1811                    crate::ffi::SEXP::scalar_real(s as f64)
1812                }
1813            })
1814            .unwrap_or(crate::ffi::SEXP::null())
1815    }
1816
1817    const HAS_MIN: bool = true;
1818
1819    fn min(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
1820        let data =
1821            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1822        crate::altrep_data::AltIntegerData::min(data, narm)
1823            .map(crate::ffi::SEXP::scalar_integer)
1824            .unwrap_or(crate::ffi::SEXP::null())
1825    }
1826
1827    const HAS_MAX: bool = true;
1828
1829    fn max(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
1830        let data =
1831            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1832        crate::altrep_data::AltIntegerData::max(data, narm)
1833            .map(crate::ffi::SEXP::scalar_integer)
1834            .unwrap_or(crate::ffi::SEXP::null())
1835    }
1836}
1837
1838crate::impl_inferbase_integer!(&'static [i32]);
1839
1840// Real static slices
1841impl crate::altrep_traits::Altrep for &'static [f64] {
1842    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1843        let data =
1844            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1845        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
1846    }
1847}
1848
1849impl crate::altrep_traits::AltVec for &'static [f64] {
1850    const HAS_DATAPTR: bool = true;
1851
1852    fn dataptr(x: crate::ffi::SEXP, writable: bool) -> *mut std::ffi::c_void {
1853        assert!(
1854            !writable,
1855            "cannot get writable DATAPTR for static ALTREP data"
1856        );
1857        let data =
1858            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1859        data.as_ptr().cast::<std::ffi::c_void>().cast_mut()
1860    }
1861
1862    const HAS_DATAPTR_OR_NULL: bool = true;
1863
1864    fn dataptr_or_null(x: crate::ffi::SEXP) -> *const std::ffi::c_void {
1865        let data =
1866            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1867        data.as_ptr().cast::<std::ffi::c_void>()
1868    }
1869}
1870
1871impl crate::altrep_traits::AltReal for &'static [f64] {
1872    const HAS_ELT: bool = true;
1873
1874    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> f64 {
1875        let data =
1876            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1877        crate::altrep_data::AltRealData::elt(data, i.max(0) as usize)
1878    }
1879
1880    const HAS_GET_REGION: bool = true;
1881
1882    fn get_region(
1883        x: crate::ffi::SEXP,
1884        start: crate::ffi::R_xlen_t,
1885        len: crate::ffi::R_xlen_t,
1886        buf: &mut [f64],
1887    ) -> crate::ffi::R_xlen_t {
1888        if start < 0 || len <= 0 {
1889            return 0;
1890        }
1891        let data =
1892            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1893        let len = len as usize;
1894        crate::altrep_data::AltRealData::get_region(data, start as usize, len, buf)
1895            as crate::ffi::R_xlen_t
1896    }
1897
1898    const HAS_NO_NA: bool = true;
1899
1900    fn no_na(x: crate::ffi::SEXP) -> i32 {
1901        let data =
1902            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1903        crate::altrep_data::AltRealData::no_na(data)
1904            .map(|b| if b { 1 } else { 0 })
1905            .unwrap_or(0)
1906    }
1907
1908    const HAS_SUM: bool = true;
1909
1910    // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
1911    fn sum(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
1912        let data =
1913            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1914        crate::altrep_data::AltRealData::sum(data, narm)
1915            .map(crate::ffi::SEXP::scalar_real)
1916            .unwrap_or(crate::ffi::SEXP::null())
1917    }
1918
1919    const HAS_MIN: bool = true;
1920
1921    fn min(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
1922        let data =
1923            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1924        crate::altrep_data::AltRealData::min(data, narm)
1925            .map(crate::ffi::SEXP::scalar_real)
1926            .unwrap_or(crate::ffi::SEXP::null())
1927    }
1928
1929    const HAS_MAX: bool = true;
1930
1931    fn max(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
1932        let data =
1933            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1934        crate::altrep_data::AltRealData::max(data, narm)
1935            .map(crate::ffi::SEXP::scalar_real)
1936            .unwrap_or(crate::ffi::SEXP::null())
1937    }
1938}
1939
1940crate::impl_inferbase_real!(&'static [f64]);
1941
1942// Logical static slices
1943impl crate::altrep_traits::Altrep for &'static [bool] {
1944    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1945        let data = unsafe {
1946            <&'static [bool] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1947        };
1948        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
1949    }
1950}
1951
1952impl crate::altrep_traits::AltVec for &'static [bool] {}
1953
1954impl crate::altrep_traits::AltLogical for &'static [bool] {
1955    const HAS_ELT: bool = true;
1956
1957    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> i32 {
1958        let data = unsafe {
1959            <&'static [bool] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1960        };
1961        crate::altrep_data::AltLogicalData::elt(data, i.max(0) as usize).to_r_int()
1962    }
1963
1964    const HAS_NO_NA: bool = true;
1965
1966    fn no_na(x: crate::ffi::SEXP) -> i32 {
1967        let data = unsafe {
1968            <&'static [bool] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1969        };
1970        crate::altrep_data::AltLogicalData::no_na(data)
1971            .map(|b| if b { 1 } else { 0 })
1972            .unwrap_or(0)
1973    }
1974
1975    const HAS_SUM: bool = true;
1976
1977    // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
1978    fn sum(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
1979        let data = unsafe {
1980            <&'static [bool] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1981        };
1982        crate::altrep_data::AltLogicalData::sum(data, narm)
1983            .map(|s| {
1984                if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
1985                    crate::ffi::SEXP::scalar_integer(s as i32)
1986                } else {
1987                    crate::ffi::SEXP::scalar_real(s as f64)
1988                }
1989            })
1990            .unwrap_or(crate::ffi::SEXP::null())
1991    }
1992}
1993
1994crate::impl_inferbase_logical!(&'static [bool]);
1995
1996// Raw static slices
1997impl crate::altrep_traits::Altrep for &'static [u8] {
1998    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1999        let data =
2000            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2001        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2002    }
2003}
2004
2005impl crate::altrep_traits::AltVec for &'static [u8] {
2006    const HAS_DATAPTR: bool = true;
2007
2008    fn dataptr(x: crate::ffi::SEXP, writable: bool) -> *mut std::ffi::c_void {
2009        assert!(
2010            !writable,
2011            "cannot get writable DATAPTR for static ALTREP data"
2012        );
2013        let data =
2014            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2015        data.as_ptr().cast::<std::ffi::c_void>().cast_mut()
2016    }
2017
2018    const HAS_DATAPTR_OR_NULL: bool = true;
2019
2020    fn dataptr_or_null(x: crate::ffi::SEXP) -> *const std::ffi::c_void {
2021        let data =
2022            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2023        data.as_ptr().cast::<std::ffi::c_void>()
2024    }
2025}
2026
2027impl crate::altrep_traits::AltRaw for &'static [u8] {
2028    const HAS_ELT: bool = true;
2029
2030    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::Rbyte {
2031        let data =
2032            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2033        crate::altrep_data::AltRawData::elt(data, i.max(0) as usize)
2034    }
2035
2036    const HAS_GET_REGION: bool = true;
2037
2038    fn get_region(
2039        x: crate::ffi::SEXP,
2040        start: crate::ffi::R_xlen_t,
2041        len: crate::ffi::R_xlen_t,
2042        buf: &mut [u8],
2043    ) -> crate::ffi::R_xlen_t {
2044        if start < 0 || len <= 0 {
2045            return 0;
2046        }
2047        let data =
2048            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2049        let len = len as usize;
2050        crate::altrep_data::AltRawData::get_region(data, start as usize, len, buf)
2051            as crate::ffi::R_xlen_t
2052    }
2053}
2054
2055crate::impl_inferbase_raw!(&'static [u8]);
2056
2057// String static slices (owned strings)
2058impl crate::altrep_traits::Altrep for &'static [String] {
2059    // String ALTREP elt calls Rf_mkCharLenCE (R API) — must use RUnwind.
2060    const GUARD: crate::altrep_traits::AltrepGuard = crate::altrep_traits::AltrepGuard::RUnwind;
2061
2062    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2063        let data = unsafe {
2064            <&'static [String] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2065        };
2066        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2067    }
2068}
2069
2070impl crate::altrep_traits::AltVec for &'static [String] {}
2071
2072impl crate::altrep_traits::AltString for &'static [String] {
2073    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::SEXP {
2074        let data = unsafe {
2075            <&'static [String] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2076        };
2077        match crate::altrep_data::AltStringData::elt(data, i.max(0) as usize) {
2078            Some(s) => unsafe { checked_mkchar(s) },
2079            None => crate::ffi::SEXP::na_string(),
2080        }
2081    }
2082
2083    const HAS_NO_NA: bool = true;
2084
2085    fn no_na(x: crate::ffi::SEXP) -> i32 {
2086        let data = unsafe {
2087            <&'static [String] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2088        };
2089        crate::altrep_data::AltStringData::no_na(data)
2090            .map(|b| if b { 1 } else { 0 })
2091            .unwrap_or(0)
2092    }
2093}
2094
2095crate::impl_inferbase_string!(&'static [String]);
2096
2097// String static slices (str references)
2098impl crate::altrep_traits::Altrep for &'static [&'static str] {
2099    // String ALTREP elt calls Rf_mkCharLenCE (R API) — must use RUnwind.
2100    const GUARD: crate::altrep_traits::AltrepGuard = crate::altrep_traits::AltrepGuard::RUnwind;
2101
2102    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2103        let data = unsafe {
2104            <&'static [&'static str] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2105        };
2106        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2107    }
2108}
2109
2110impl crate::altrep_traits::AltVec for &'static [&'static str] {}
2111
2112impl crate::altrep_traits::AltString for &'static [&'static str] {
2113    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::SEXP {
2114        let data = unsafe {
2115            <&'static [&'static str] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2116        };
2117        match crate::altrep_data::AltStringData::elt(data, i.max(0) as usize) {
2118            Some(s) => unsafe { checked_mkchar(s) },
2119            None => crate::ffi::SEXP::na_string(),
2120        }
2121    }
2122
2123    const HAS_NO_NA: bool = true;
2124
2125    fn no_na(x: crate::ffi::SEXP) -> i32 {
2126        let data = unsafe {
2127            <&'static [&'static str] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2128        };
2129        crate::altrep_data::AltStringData::no_na(data)
2130            .map(|b| if b { 1 } else { 0 })
2131            .unwrap_or(0)
2132    }
2133}
2134
2135crate::impl_inferbase_string!(&'static [&'static str]);
2136// endregion
2137
2138// region: RegisterAltrep implementations for builtin types
2139//
2140// These implementations provide ALTREP class registration for Vec<T>, Box<[T]>,
2141// and Range<T> types. They allow using these types with ALTREP via wrapper structs.
2142//
2143// Note: IntoR is NOT implemented here for Vec types because there are already
2144// existing IntoR implementations that copy data to R eagerly. To get ALTREP
2145// behavior, use wrapper structs:
2146//   #[miniextendr(class = "MyVec")]
2147//   pub struct MyVecClass(pub Vec<i32>);
2148//
2149// Each type uses a static OnceLock to cache the ALTREP class handle, which is
2150// registered on first use with the current package's name (from ALTREP_PKG_NAME).
2151
2152use crate::altrep::RegisterAltrep;
2153
2154/// Helper macro to implement RegisterAltrep for a builtin type.
2155macro_rules! impl_register_altrep_builtin {
2156    ($ty:ty, $class_name:expr) => {
2157        impl RegisterAltrep for $ty {
2158            fn get_or_init_class() -> crate::ffi::altrep::R_altrep_class_t {
2159                use std::sync::OnceLock;
2160                static CLASS: OnceLock<crate::ffi::altrep::R_altrep_class_t> = OnceLock::new();
2161                *CLASS.get_or_init(|| {
2162                    // Class name as null-terminated C string
2163                    const CLASS_NAME: &[u8] = concat!($class_name, "\0").as_bytes();
2164                    let cls = unsafe {
2165                        <$ty as crate::altrep_data::InferBase>::make_class(
2166                            CLASS_NAME.as_ptr().cast::<std::ffi::c_char>(),
2167                            crate::AltrepPkgName::as_ptr(),
2168                        )
2169                    };
2170                    unsafe {
2171                        <$ty as crate::altrep_data::InferBase>::install_methods(cls);
2172                    }
2173                    cls
2174                })
2175            }
2176        }
2177    };
2178}
2179
2180// Vec types - RegisterAltrep only (IntoR exists elsewhere, copies data)
2181impl_register_altrep_builtin!(Vec<i32>, "Vec_i32");
2182impl_register_altrep_builtin!(Vec<f64>, "Vec_f64");
2183impl_register_altrep_builtin!(Vec<bool>, "Vec_bool");
2184impl_register_altrep_builtin!(Vec<u8>, "Vec_u8");
2185impl_register_altrep_builtin!(Vec<String>, "Vec_String");
2186impl_register_altrep_builtin!(Vec<Option<String>>, "Vec_Option_String");
2187impl_register_altrep_builtin!(Vec<crate::ffi::Rcomplex>, "Vec_Rcomplex");
2188
2189// Range types - RegisterAltrep only
2190impl_register_altrep_builtin!(std::ops::Range<i32>, "Range_i32");
2191impl_register_altrep_builtin!(std::ops::Range<i64>, "Range_i64");
2192impl_register_altrep_builtin!(std::ops::Range<f64>, "Range_f64");
2193
2194// Box types - RegisterAltrep only
2195impl_register_altrep_builtin!(Box<[i32]>, "Box_i32");
2196impl_register_altrep_builtin!(Box<[f64]>, "Box_f64");
2197impl_register_altrep_builtin!(Box<[bool]>, "Box_bool");
2198impl_register_altrep_builtin!(Box<[u8]>, "Box_u8");
2199impl_register_altrep_builtin!(Box<[String]>, "Box_String");
2200impl_register_altrep_builtin!(Box<[crate::ffi::Rcomplex]>, "Box_Rcomplex");
2201
2202// Cow types - RegisterAltrep for zero-copy borrow from R
2203impl_register_altrep_builtin!(std::borrow::Cow<'static, [i32]>, "Cow_i32");
2204impl_register_altrep_builtin!(std::borrow::Cow<'static, [f64]>, "Cow_f64");
2205impl_register_altrep_builtin!(std::borrow::Cow<'static, [u8]>, "Cow_u8");
2206impl_register_altrep_builtin!(
2207    std::borrow::Cow<'static, [crate::ffi::Rcomplex]>,
2208    "Cow_Rcomplex"
2209);
2210
2211// Cow string vector types
2212impl_register_altrep_builtin!(Vec<std::borrow::Cow<'static, str>>, "Vec_Cow_str");
2213impl_register_altrep_builtin!(
2214    Vec<Option<std::borrow::Cow<'static, str>>>,
2215    "Vec_Option_Cow_str"
2216);
2217// endregion