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_raw(&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                    // Scoped block: the &T borrow must end before altrep_extract_mut
282                    // to avoid aliasing &T / &mut T (Stacked Borrows UB).
283                    let ro = {
284                        let d = unsafe {
285                            <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
286                        };
287                        <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr_or_null(d)
288                    };
289                    if let Some(p) = ro {
290                        return p.cast_mut().cast::<core::ffi::c_void>();
291                    }
292                    // dataptr_or_null returned None — try mutable path.
293                    let d = unsafe {
294                        <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
295                    };
296                    <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr(d, false)
297                        .map(|p| p.cast::<core::ffi::c_void>())
298                };
299
300                if let Some(p) = direct {
301                    return p;
302                }
303
304                // The underlying data can't provide a contiguous pointer (e.g., Arrow
305                // array with null bitmask). Materialize into data2 via Elt methods.
306                // Must never return null — R doesn't fall back when custom Dataptr is set.
307                unsafe { $crate::altrep_data::materialize_altrep_data2::<$elem>(x) }
308            }
309
310            const HAS_DATAPTR_OR_NULL: bool = true;
311
312            fn dataptr_or_null(x: $crate::ffi::SEXP) -> *const core::ffi::c_void {
313                // Check data2 cache first (may have been materialized by a prior dataptr call)
314                unsafe {
315                    let data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2_raw(&x);
316                    if !data2.is_null()
317                        && $crate::ffi::SexpExt::type_of(&data2)
318                            == <$elem as $crate::ffi::RNativeType>::SEXP_TYPE
319                    {
320                        return $crate::ffi::DATAPTR_RO(data2);
321                    }
322                }
323                let d =
324                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
325                <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr_or_null(d)
326                    .map(|p| p.cast::<core::ffi::c_void>())
327                    .unwrap_or(core::ptr::null())
328            }
329        }
330    };
331}
332
333/// Internal macro: impl AltVec with dataptr support for string ALTREP.
334///
335/// String vectors (STRSXP) store CHARSXP pointers, not contiguous data. This macro
336/// materializes remaining uncached elements into the data2 STRSXP cache (which may
337/// already have some elements from prior `Elt` calls). Returns the cached STRSXP's
338/// data pointer.
339#[macro_export]
340#[doc(hidden)]
341macro_rules! __impl_altvec_string_dataptr {
342    ($ty:ty) => {
343        impl $crate::altrep_traits::AltVec for $ty {
344            const HAS_DATAPTR: bool = true;
345
346            fn dataptr(x: $crate::ffi::SEXP, _writable: bool) -> *mut core::ffi::c_void {
347                unsafe {
348                    let n = <$ty as $crate::altrep_traits::Altrep>::length(x);
349
350                    // Get or allocate the data2 cache STRSXP
351                    let mut data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2_raw(&x);
352                    let fresh_alloc = data2.is_null()
353                        || $crate::ffi::SexpExt::type_of(&data2) != $crate::ffi::SEXPTYPE::STRSXP;
354                    if fresh_alloc {
355                        // Rf_allocVector(STRSXP, n) leaves elements UNINITIALIZED
356                        // (garbage SEXP pointers). Must fill with R_NaString sentinel
357                        // so cache lookups work. This is O(n) but unavoidable.
358                        data2 = $crate::ffi::Rf_protect($crate::ffi::Rf_allocVector(
359                            $crate::ffi::SEXPTYPE::STRSXP,
360                            n,
361                        ));
362                        for j in 0..n {
363                            $crate::ffi::SexpExt::set_string_elt(
364                                &data2,
365                                j,
366                                $crate::ffi::SEXP::na_string(),
367                            );
368                        }
369                        $crate::altrep_ext::AltrepSexpExt::set_altrep_data2(&x, data2);
370                        $crate::ffi::Rf_unprotect(1);
371                    }
372
373                    // Fill uncached elements only — elements already cached by Elt
374                    // are non-NA CHARSXPs and are skipped. NA elements are re-probed
375                    // from Rust (O(1)) to handle mixed cached/uncached NA slots.
376                    for i in 0..n {
377                        let cached = $crate::ffi::SexpExt::string_elt(&data2, i);
378                        if cached != $crate::ffi::SEXP::na_string() {
379                            continue; // already cached by a prior Elt call
380                        }
381                        // Compute from Rust and store
382                        let elt = <$ty as $crate::altrep_traits::AltString>::elt(x, i);
383                        $crate::ffi::SexpExt::set_string_elt(&data2, i, elt);
384                    }
385
386                    $crate::ffi::DATAPTR_RO(data2).cast_mut()
387                }
388            }
389
390            const HAS_DATAPTR_OR_NULL: bool = true;
391
392            fn dataptr_or_null(x: $crate::ffi::SEXP) -> *const core::ffi::c_void {
393                // Always return null. The data2 STRSXP may be partially cached
394                // (Elt filled some slots, others are R_NaString sentinels).
395                // Returning a pointer to a partial cache would expose sentinel
396                // R_NaString as actual NAs. Returning null tells R to use
397                // Elt-based access, which correctly handles the per-element cache.
398                // Dataptr (not dataptr_or_null) is the full-materialization path.
399                let _ = x;
400                core::ptr::null()
401            }
402        }
403    };
404}
405
406/// Internal macro: impl AltVec with materializing dataptr for logical ALTREP.
407///
408/// Thin wrapper: provides a trivial `AltrepDataptr` (no direct pointer) and
409/// delegates to `__impl_altvec_dataptr` which materializes via `RNativeType::elt`.
410/// R logicals are stored as `i32` but accessed through `RLogical` for type safety.
411#[macro_export]
412#[doc(hidden)]
413macro_rules! __impl_altvec_logical_dataptr {
414    ($ty:ty) => {
415        impl $crate::altrep_data::AltrepDataptr<$crate::ffi::RLogical> for $ty {
416            fn dataptr(&mut self, _writable: bool) -> Option<*mut $crate::ffi::RLogical> {
417                None
418            }
419        }
420        $crate::__impl_altvec_dataptr!($ty, $crate::ffi::RLogical);
421    };
422}
423
424/// Internal macro: impl AltVec with materializing dataptr for integer ALTREP.
425///
426/// Internal macro: impl AltVec with materializing dataptr for integer ALTREP.
427///
428/// Thin wrapper: provides a trivial `AltrepDataptr` (no direct pointer) and
429/// delegates to `__impl_altvec_dataptr` which materializes via `RNativeType::elt`.
430#[macro_export]
431#[doc(hidden)]
432macro_rules! __impl_altvec_integer_dataptr {
433    ($ty:ty) => {
434        impl $crate::altrep_data::AltrepDataptr<i32> for $ty {
435            fn dataptr(&mut self, _writable: bool) -> Option<*mut i32> {
436                None
437            }
438        }
439        $crate::__impl_altvec_dataptr!($ty, i32);
440    };
441}
442
443/// Internal macro: impl AltVec with materializing dataptr for real ALTREP.
444#[macro_export]
445#[doc(hidden)]
446macro_rules! __impl_altvec_real_dataptr {
447    ($ty:ty) => {
448        impl $crate::altrep_data::AltrepDataptr<f64> for $ty {
449            fn dataptr(&mut self, _writable: bool) -> Option<*mut f64> {
450                None
451            }
452        }
453        $crate::__impl_altvec_dataptr!($ty, f64);
454    };
455}
456
457/// Internal macro: impl AltVec with materializing dataptr for raw ALTREP.
458#[macro_export]
459#[doc(hidden)]
460macro_rules! __impl_altvec_raw_dataptr {
461    ($ty:ty) => {
462        impl $crate::altrep_data::AltrepDataptr<u8> for $ty {
463            fn dataptr(&mut self, _writable: bool) -> Option<*mut u8> {
464                None
465            }
466        }
467        $crate::__impl_altvec_dataptr!($ty, u8);
468    };
469}
470
471/// Internal macro: impl AltVec with materializing dataptr for complex ALTREP.
472#[macro_export]
473#[doc(hidden)]
474macro_rules! __impl_altvec_complex_dataptr {
475    ($ty:ty) => {
476        impl $crate::altrep_data::AltrepDataptr<$crate::ffi::Rcomplex> for $ty {
477            fn dataptr(&mut self, _writable: bool) -> Option<*mut $crate::ffi::Rcomplex> {
478                None
479            }
480        }
481        $crate::__impl_altvec_dataptr!($ty, $crate::ffi::Rcomplex);
482    };
483}
484
485/// Internal macro: impl AltVec with extract_subset support
486#[macro_export]
487#[doc(hidden)]
488macro_rules! __impl_altvec_extract_subset {
489    ($ty:ty) => {
490        impl $crate::altrep_traits::AltVec for $ty {
491            const HAS_EXTRACT_SUBSET: bool = true;
492
493            fn extract_subset(
494                x: $crate::ffi::SEXP,
495                indx: $crate::ffi::SEXP,
496                _call: $crate::ffi::SEXP,
497            ) -> $crate::ffi::SEXP {
498                // Validate that indx is an integer vector before calling INTEGER().
499                // Return NULL to signal R to use default subsetting if not.
500                if $crate::ffi::SexpExt::type_of(&indx) != $crate::ffi::SEXPTYPE::INTSXP {
501                    return core::ptr::null_mut();
502                }
503
504                // Convert indx SEXP to slice using SexpExt (avoids raw-ptr-deref lint)
505                let indices = unsafe { $crate::ffi::SexpExt::as_slice::<i32>(&indx) };
506
507                let data =
508                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
509                <$ty as $crate::altrep_data::AltrepExtractSubset>::extract_subset(data, indices)
510                    .unwrap_or($crate::ffi::SEXP::nil())
511            }
512        }
513    };
514}
515// endregion
516
517// region: Shared building-block macros for ALTREP trait implementations
518//
519// These macros expand to associated items inside `impl` blocks. They are
520// invoked by the per-family `__impl_alt*_methods!` macros to eliminate
521// code duplication across the 7 ALTREP type families.
522
523/// Shared `elt` implementation for ALTREP families with direct element access.
524///
525/// Generates `const HAS_ELT` and `fn elt(...)` inside an impl block.
526/// Used by integer, real, raw, and complex families.
527#[macro_export]
528#[doc(hidden)]
529macro_rules! __impl_alt_elt {
530    ($ty:ty, $trait:path, $elem:ty, $na:expr) => {
531        const HAS_ELT: bool = true;
532
533        fn elt(x: $crate::ffi::SEXP, i: $crate::ffi::R_xlen_t) -> $elem {
534            let data =
535                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
536            <$ty as $trait>::elt(data, i.max(0) as usize)
537        }
538    };
539}
540
541/// Shared `get_region` implementation for ALTREP families.
542///
543/// Generates `const HAS_GET_REGION` and `fn get_region(...)` inside an impl block.
544/// Used by integer, real, logical, raw, and complex families.
545#[macro_export]
546#[doc(hidden)]
547macro_rules! __impl_alt_get_region {
548    ($ty:ty, $trait:path, $buf_ty:ty) => {
549        const HAS_GET_REGION: bool = true;
550
551        fn get_region(
552            x: $crate::ffi::SEXP,
553            start: $crate::ffi::R_xlen_t,
554            len: $crate::ffi::R_xlen_t,
555            buf: &mut [$buf_ty],
556        ) -> $crate::ffi::R_xlen_t {
557            if start < 0 || len <= 0 {
558                return 0;
559            }
560            let data =
561                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
562            let len = len as usize;
563            <$ty as $trait>::get_region(data, start as usize, len, buf) as $crate::ffi::R_xlen_t
564        }
565    };
566}
567
568/// Shared `is_sorted` implementation for ALTREP families.
569///
570/// Generates `const HAS_IS_SORTED` and `fn is_sorted(...)` inside an impl block.
571/// Used by integer, real, logical, and string families.
572#[macro_export]
573#[doc(hidden)]
574macro_rules! __impl_alt_is_sorted {
575    ($ty:ty, $trait:path) => {
576        const HAS_IS_SORTED: bool = true;
577
578        fn is_sorted(x: $crate::ffi::SEXP) -> i32 {
579            let data =
580                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
581            <$ty as $trait>::is_sorted(data)
582                .map(|s| s.to_r_int())
583                .unwrap_or(i32::MIN)
584        }
585    };
586}
587
588/// Shared `no_na` implementation for ALTREP families.
589///
590/// Generates `const HAS_NO_NA` and `fn no_na(...)` inside an impl block.
591/// Used by integer, real, logical, and string families.
592#[macro_export]
593#[doc(hidden)]
594macro_rules! __impl_alt_no_na {
595    ($ty:ty, $trait:path) => {
596        const HAS_NO_NA: bool = true;
597
598        fn no_na(x: $crate::ffi::SEXP) -> i32 {
599            let data =
600                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
601            <$ty as $trait>::no_na(data)
602                .map(|b| if b { 1 } else { 0 })
603                .unwrap_or(0)
604        }
605    };
606}
607// endregion
608
609// region: Parametric macro: __impl_alt_from_data!
610//
611// This internal macro generates the standard ALTREP trait implementations
612// (Altrep, AltVec, family-specific methods, InferBase) for a given type.
613// The 7 public `impl_alt*_from_data!` macros delegate to this with
614// family-specific parameters.
615
616#[macro_export]
617#[doc(hidden)]
618macro_rules! __impl_alt_from_data {
619    // Base: no options
620    ($ty:ty, $methods:ident, $inferbase:ident) => {
621        $crate::__impl_altrep_base!($ty);
622        impl $crate::altrep_traits::AltVec for $ty {}
623        $crate::$methods!($ty);
624        $crate::$inferbase!($ty);
625    };
626    // Base: explicit guard
627    ($ty:ty, $methods:ident, $inferbase:ident, @guard $guard:ident) => {
628        $crate::__impl_altrep_base!($ty, $guard);
629        impl $crate::altrep_traits::AltVec for $ty {}
630        $crate::$methods!($ty);
631        $crate::$inferbase!($ty);
632    };
633    // Dataptr with element type
634    ($ty:ty, $methods:ident, $inferbase:ident, dataptr($elem:ty)) => {
635        $crate::__impl_altrep_base!($ty);
636        $crate::__impl_altvec_dataptr!($ty, $elem);
637        $crate::$methods!($ty);
638        $crate::$inferbase!($ty);
639    };
640    // String dataptr (materialization into STRSXP)
641    ($ty:ty, $methods:ident, $inferbase:ident, string_dataptr) => {
642        $crate::__impl_altrep_base!($ty);
643        $crate::__impl_altvec_string_dataptr!($ty);
644        $crate::$methods!($ty);
645        $crate::$inferbase!($ty);
646    };
647    // String dataptr + explicit guard
648    ($ty:ty, $methods:ident, $inferbase:ident, string_dataptr, @guard $guard:ident) => {
649        $crate::__impl_altrep_base!($ty, $guard);
650        $crate::__impl_altvec_string_dataptr!($ty);
651        $crate::$methods!($ty);
652        $crate::$inferbase!($ty);
653    };
654    // Serialize only
655    ($ty:ty, $methods:ident, $inferbase:ident, serialize) => {
656        $crate::__impl_altrep_base_with_serialize!($ty);
657        impl $crate::altrep_traits::AltVec for $ty {}
658        $crate::$methods!($ty);
659        $crate::$inferbase!($ty);
660    };
661    // Serialize + explicit guard
662    ($ty:ty, $methods:ident, $inferbase:ident, serialize, @guard $guard:ident) => {
663        $crate::__impl_altrep_base_with_serialize!($ty, $guard);
664        impl $crate::altrep_traits::AltVec for $ty {}
665        $crate::$methods!($ty);
666        $crate::$inferbase!($ty);
667    };
668    // Subset only
669    ($ty:ty, $methods:ident, $inferbase:ident, subset) => {
670        $crate::__impl_altrep_base!($ty);
671        $crate::__impl_altvec_extract_subset!($ty);
672        $crate::$methods!($ty);
673        $crate::$inferbase!($ty);
674    };
675    // Dataptr + serialize
676    ($ty:ty, $methods:ident, $inferbase:ident, dataptr($elem:ty), serialize) => {
677        $crate::__impl_altrep_base_with_serialize!($ty);
678        $crate::__impl_altvec_dataptr!($ty, $elem);
679        $crate::$methods!($ty);
680        $crate::$inferbase!($ty);
681    };
682    // String dataptr + serialize
683    ($ty:ty, $methods:ident, $inferbase:ident, string_dataptr, serialize) => {
684        $crate::__impl_altrep_base_with_serialize!($ty);
685        $crate::__impl_altvec_string_dataptr!($ty);
686        $crate::$methods!($ty);
687        $crate::$inferbase!($ty);
688    };
689    // String dataptr + serialize + explicit guard
690    ($ty:ty, $methods:ident, $inferbase:ident, string_dataptr, serialize, @guard $guard:ident) => {
691        $crate::__impl_altrep_base_with_serialize!($ty, $guard);
692        $crate::__impl_altvec_string_dataptr!($ty);
693        $crate::$methods!($ty);
694        $crate::$inferbase!($ty);
695    };
696    // Subset + serialize
697    ($ty:ty, $methods:ident, $inferbase:ident, subset, serialize) => {
698        $crate::__impl_altrep_base_with_serialize!($ty);
699        $crate::__impl_altvec_extract_subset!($ty);
700        $crate::$methods!($ty);
701        $crate::$inferbase!($ty);
702    };
703}
704// endregion
705
706// region: Per-family method macros (using shared building blocks)
707
708/// Internal macro for AltInteger method implementations.
709#[macro_export]
710#[doc(hidden)]
711macro_rules! __impl_altinteger_methods {
712    ($ty:ty) => {
713        impl $crate::altrep_traits::AltInteger for $ty {
714            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltIntegerData, i32, i32::MIN);
715            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltIntegerData, i32);
716            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltIntegerData);
717            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltIntegerData);
718
719            const HAS_SUM: bool = true;
720
721            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
722            fn sum(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
723                let data =
724                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
725                match <$ty as $crate::altrep_data::AltIntegerData>::sum(data, narm) {
726                    Some(s) => {
727                        if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
728                            $crate::ffi::SEXP::scalar_integer(s as i32)
729                        } else {
730                            $crate::ffi::SEXP::scalar_real(s as f64)
731                        }
732                    }
733                    None => $crate::ffi::SEXP::null(),
734                }
735            }
736
737            const HAS_MIN: bool = true;
738
739            fn min(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
740                let data =
741                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
742                <$ty as $crate::altrep_data::AltIntegerData>::min(data, narm)
743                    .map(|m| $crate::ffi::SEXP::scalar_integer(m))
744                    .unwrap_or($crate::ffi::SEXP::null())
745            }
746
747            const HAS_MAX: bool = true;
748
749            fn max(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
750                let data =
751                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
752                <$ty as $crate::altrep_data::AltIntegerData>::max(data, narm)
753                    .map(|m| $crate::ffi::SEXP::scalar_integer(m))
754                    .unwrap_or($crate::ffi::SEXP::null())
755            }
756        }
757    };
758}
759
760/// Generate ALTREP trait implementations for a type that implements AltRealData.
761///
762/// ## Variants
763///
764/// ```ignore
765/// // Basic (no dataptr, no serialization):
766/// impl_altreal_from_data!(MyType);
767///
768/// // With dataptr (type must implement AltrepDataptr<f64>):
769/// impl_altreal_from_data!(MyType, dataptr);
770///
771/// // With serialization (type must implement AltrepSerialize):
772/// impl_altreal_from_data!(MyType, serialize);
773///
774/// // With subset optimization (type must implement AltrepExtractSubset):
775/// impl_altreal_from_data!(MyType, subset);
776///
777/// // Combine multiple options:
778/// impl_altreal_from_data!(MyType, dataptr, serialize);
779/// impl_altreal_from_data!(MyType, subset, serialize);
780/// ```
781#[macro_export]
782macro_rules! impl_altreal_from_data {
783    ($ty:ty) => {
784        $crate::__impl_altrep_base!($ty);
785        $crate::__impl_altvec_real_dataptr!($ty);
786        $crate::__impl_altreal_methods!($ty);
787        $crate::impl_inferbase_real!($ty);
788    };
789    ($ty:ty, dataptr) => {
790        $crate::__impl_alt_from_data!(
791            $ty,
792            __impl_altreal_methods,
793            impl_inferbase_real,
794            dataptr(f64)
795        );
796    };
797    ($ty:ty, serialize) => {
798        $crate::__impl_altrep_base_with_serialize!($ty);
799        $crate::__impl_altvec_real_dataptr!($ty);
800        $crate::__impl_altreal_methods!($ty);
801        $crate::impl_inferbase_real!($ty);
802    };
803    ($ty:ty, dataptr, serialize) => {
804        $crate::__impl_alt_from_data!(
805            $ty,
806            __impl_altreal_methods,
807            impl_inferbase_real,
808            dataptr(f64),
809            serialize
810        );
811    };
812    ($ty:ty, serialize, dataptr) => {
813        $crate::impl_altreal_from_data!($ty, dataptr, serialize);
814    };
815    ($ty:ty, subset) => {
816        $crate::__impl_alt_from_data!($ty, __impl_altreal_methods, impl_inferbase_real, subset);
817    };
818    ($ty:ty, subset, serialize) => {
819        $crate::__impl_alt_from_data!(
820            $ty,
821            __impl_altreal_methods,
822            impl_inferbase_real,
823            subset,
824            serialize
825        );
826    };
827    ($ty:ty, serialize, subset) => {
828        $crate::impl_altreal_from_data!($ty, subset, serialize);
829    };
830    // Materializing dataptr only (no serialization)
831    ($ty:ty, materializing_dataptr) => {
832        $crate::__impl_altrep_base!($ty);
833        $crate::__impl_altvec_real_dataptr!($ty);
834        $crate::__impl_altreal_methods!($ty);
835        $crate::impl_inferbase_real!($ty);
836    };
837    // Materializing dataptr + serialize (for computed types like Range<f64>)
838    ($ty:ty, materializing_dataptr, serialize) => {
839        $crate::__impl_altrep_base_with_serialize!($ty);
840        $crate::__impl_altvec_real_dataptr!($ty);
841        $crate::__impl_altreal_methods!($ty);
842        $crate::impl_inferbase_real!($ty);
843    };
844}
845
846/// Internal macro for AltReal method implementations.
847#[macro_export]
848#[doc(hidden)]
849macro_rules! __impl_altreal_methods {
850    ($ty:ty) => {
851        impl $crate::altrep_traits::AltReal for $ty {
852            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltRealData, f64, f64::NAN);
853            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltRealData, f64);
854            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltRealData);
855            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltRealData);
856
857            const HAS_SUM: bool = true;
858
859            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
860            fn sum(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
861                let data =
862                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
863                <$ty as $crate::altrep_data::AltRealData>::sum(data, narm)
864                    .map(|s| $crate::ffi::SEXP::scalar_real(s))
865                    .unwrap_or($crate::ffi::SEXP::null())
866            }
867
868            const HAS_MIN: bool = true;
869
870            fn min(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
871                let data =
872                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
873                <$ty as $crate::altrep_data::AltRealData>::min(data, narm)
874                    .map(|m| $crate::ffi::SEXP::scalar_real(m))
875                    .unwrap_or($crate::ffi::SEXP::null())
876            }
877
878            const HAS_MAX: bool = true;
879
880            fn max(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
881                let data =
882                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
883                <$ty as $crate::altrep_data::AltRealData>::max(data, narm)
884                    .map(|m| $crate::ffi::SEXP::scalar_real(m))
885                    .unwrap_or($crate::ffi::SEXP::null())
886            }
887        }
888    };
889}
890
891/// Generate ALTREP trait implementations for a type that implements AltLogicalData.
892///
893/// ## Variants
894///
895/// ```ignore
896/// // Basic (no dataptr, no serialization):
897/// impl_altlogical_from_data!(MyType);
898///
899/// // With dataptr (type must implement AltrepDataptr<i32>):
900/// impl_altlogical_from_data!(MyType, dataptr);
901///
902/// // With serialization (type must implement AltrepSerialize):
903/// impl_altlogical_from_data!(MyType, serialize);
904///
905/// // With subset optimization (type must implement AltrepExtractSubset):
906/// impl_altlogical_from_data!(MyType, subset);
907///
908/// // Combine multiple options:
909/// impl_altlogical_from_data!(MyType, dataptr, serialize);
910/// impl_altlogical_from_data!(MyType, subset, serialize);
911/// ```
912#[macro_export]
913macro_rules! impl_altlogical_from_data {
914    ($ty:ty) => {
915        $crate::__impl_altrep_base!($ty);
916        $crate::__impl_altvec_logical_dataptr!($ty);
917        $crate::__impl_altlogical_methods!($ty);
918        $crate::impl_inferbase_logical!($ty);
919    };
920    ($ty:ty, dataptr) => {
921        $crate::__impl_alt_from_data!(
922            $ty,
923            __impl_altlogical_methods,
924            impl_inferbase_logical,
925            dataptr(i32)
926        );
927    };
928    ($ty:ty, serialize) => {
929        $crate::__impl_altrep_base_with_serialize!($ty);
930        $crate::__impl_altvec_logical_dataptr!($ty);
931        $crate::__impl_altlogical_methods!($ty);
932        $crate::impl_inferbase_logical!($ty);
933    };
934    ($ty:ty, dataptr, serialize) => {
935        $crate::__impl_alt_from_data!(
936            $ty,
937            __impl_altlogical_methods,
938            impl_inferbase_logical,
939            dataptr(i32),
940            serialize
941        );
942    };
943    ($ty:ty, serialize, dataptr) => {
944        $crate::impl_altlogical_from_data!($ty, dataptr, serialize);
945    };
946    ($ty:ty, subset) => {
947        $crate::__impl_alt_from_data!(
948            $ty,
949            __impl_altlogical_methods,
950            impl_inferbase_logical,
951            subset
952        );
953    };
954    ($ty:ty, subset, serialize) => {
955        $crate::__impl_alt_from_data!(
956            $ty,
957            __impl_altlogical_methods,
958            impl_inferbase_logical,
959            subset,
960            serialize
961        );
962    };
963    ($ty:ty, serialize, subset) => {
964        $crate::impl_altlogical_from_data!($ty, subset, serialize);
965    };
966    // Materializing dataptr only (no serialization)
967    ($ty:ty, materializing_dataptr) => {
968        $crate::__impl_altrep_base!($ty);
969        $crate::__impl_altvec_logical_dataptr!($ty);
970        $crate::__impl_altlogical_methods!($ty);
971        $crate::impl_inferbase_logical!($ty);
972    };
973    // Materializing dataptr + serialize (for bool types that need bool→i32 conversion)
974    ($ty:ty, materializing_dataptr, serialize) => {
975        $crate::__impl_altrep_base_with_serialize!($ty);
976        $crate::__impl_altvec_logical_dataptr!($ty);
977        $crate::__impl_altlogical_methods!($ty);
978        $crate::impl_inferbase_logical!($ty);
979    };
980}
981
982/// Internal macro: impl AltLogical methods from AltLogicalData
983#[macro_export]
984#[doc(hidden)]
985macro_rules! __impl_altlogical_methods {
986    ($ty:ty) => {
987        impl $crate::altrep_traits::AltLogical for $ty {
988            // Logical elt is special: returns Logical → .to_r_int()
989            const HAS_ELT: bool = true;
990
991            fn elt(x: $crate::ffi::SEXP, i: $crate::ffi::R_xlen_t) -> i32 {
992                let data =
993                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
994                <$ty as $crate::altrep_data::AltLogicalData>::elt(data, i.max(0) as usize)
995                    .to_r_int()
996            }
997
998            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltLogicalData, i32);
999            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltLogicalData);
1000            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltLogicalData);
1001
1002            const HAS_SUM: bool = true;
1003
1004            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
1005            fn sum(x: $crate::ffi::SEXP, narm: bool) -> $crate::ffi::SEXP {
1006                let data =
1007                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1008                match <$ty as $crate::altrep_data::AltLogicalData>::sum(data, narm) {
1009                    Some(s) => {
1010                        if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
1011                            $crate::ffi::SEXP::scalar_integer(s as i32)
1012                        } else {
1013                            $crate::ffi::SEXP::scalar_real(s as f64)
1014                        }
1015                    }
1016                    None => $crate::ffi::SEXP::null(),
1017                }
1018            }
1019        }
1020    };
1021}
1022
1023/// Generate ALTREP trait implementations for a type that implements AltRawData.
1024///
1025/// ## Variants
1026///
1027/// ```ignore
1028/// // Basic (no dataptr, no serialization):
1029/// impl_altraw_from_data!(MyType);
1030///
1031/// // With dataptr (type must implement AltrepDataptr<u8>):
1032/// impl_altraw_from_data!(MyType, dataptr);
1033///
1034/// // With serialization (type must implement AltrepSerialize):
1035/// impl_altraw_from_data!(MyType, serialize);
1036///
1037/// // With subset optimization (type must implement AltrepExtractSubset):
1038/// impl_altraw_from_data!(MyType, subset);
1039///
1040/// // Combine multiple options:
1041/// impl_altraw_from_data!(MyType, dataptr, serialize);
1042/// impl_altraw_from_data!(MyType, subset, serialize);
1043/// ```
1044#[macro_export]
1045macro_rules! impl_altraw_from_data {
1046    ($ty:ty) => {
1047        $crate::__impl_altrep_base!($ty);
1048        $crate::__impl_altvec_raw_dataptr!($ty);
1049        $crate::__impl_altraw_methods!($ty);
1050        $crate::impl_inferbase_raw!($ty);
1051    };
1052    ($ty:ty, dataptr) => {
1053        $crate::__impl_alt_from_data!($ty, __impl_altraw_methods, impl_inferbase_raw, dataptr(u8));
1054    };
1055    ($ty:ty, serialize) => {
1056        $crate::__impl_altrep_base_with_serialize!($ty);
1057        $crate::__impl_altvec_raw_dataptr!($ty);
1058        $crate::__impl_altraw_methods!($ty);
1059        $crate::impl_inferbase_raw!($ty);
1060    };
1061    ($ty:ty, dataptr, serialize) => {
1062        $crate::__impl_alt_from_data!(
1063            $ty,
1064            __impl_altraw_methods,
1065            impl_inferbase_raw,
1066            dataptr(u8),
1067            serialize
1068        );
1069    };
1070    ($ty:ty, serialize, dataptr) => {
1071        $crate::impl_altraw_from_data!($ty, dataptr, serialize);
1072    };
1073    ($ty:ty, subset) => {
1074        $crate::__impl_alt_from_data!($ty, __impl_altraw_methods, impl_inferbase_raw, subset);
1075    };
1076    ($ty:ty, subset, serialize) => {
1077        $crate::__impl_alt_from_data!(
1078            $ty,
1079            __impl_altraw_methods,
1080            impl_inferbase_raw,
1081            subset,
1082            serialize
1083        );
1084    };
1085    ($ty:ty, serialize, subset) => {
1086        $crate::impl_altraw_from_data!($ty, subset, serialize);
1087    };
1088}
1089
1090/// Internal macro for AltRaw method implementations.
1091#[macro_export]
1092#[doc(hidden)]
1093macro_rules! __impl_altraw_methods {
1094    ($ty:ty) => {
1095        impl $crate::altrep_traits::AltRaw for $ty {
1096            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltRawData, u8, 0);
1097            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltRawData, u8);
1098        }
1099    };
1100}
1101
1102/// Generate ALTREP trait implementations for a type that implements AltStringData.
1103///
1104/// ## Variants
1105///
1106/// ```ignore
1107/// // Basic (no serialization):
1108/// impl_altstring_from_data!(MyType);
1109///
1110/// // With dataptr (materialized STRSXP):
1111/// impl_altstring_from_data!(MyType, dataptr);
1112///
1113/// // With serialization (type must implement AltrepSerialize):
1114/// impl_altstring_from_data!(MyType, serialize);
1115///
1116/// // With subset optimization (type must implement AltrepExtractSubset):
1117/// impl_altstring_from_data!(MyType, subset);
1118///
1119/// // Combine multiple options:
1120/// impl_altstring_from_data!(MyType, dataptr, serialize);
1121/// impl_altstring_from_data!(MyType, subset, serialize);
1122/// ```
1123#[macro_export]
1124macro_rules! impl_altstring_from_data {
1125    ($ty:ty) => {
1126        $crate::__impl_altrep_base!($ty);
1127        $crate::__impl_altvec_string_dataptr!($ty);
1128        $crate::__impl_altstring_methods!($ty);
1129        $crate::impl_inferbase_string!($ty);
1130    };
1131    ($ty:ty, dataptr) => {
1132        $crate::__impl_alt_from_data!(
1133            $ty,
1134            __impl_altstring_methods,
1135            impl_inferbase_string,
1136            string_dataptr
1137        );
1138    };
1139    ($ty:ty, serialize) => {
1140        $crate::__impl_altrep_base_with_serialize!($ty);
1141        $crate::__impl_altvec_string_dataptr!($ty);
1142        $crate::__impl_altstring_methods!($ty);
1143        $crate::impl_inferbase_string!($ty);
1144    };
1145    ($ty:ty, dataptr, serialize) => {
1146        $crate::__impl_alt_from_data!(
1147            $ty,
1148            __impl_altstring_methods,
1149            impl_inferbase_string,
1150            string_dataptr,
1151            serialize
1152        );
1153    };
1154    ($ty:ty, subset) => {
1155        $crate::__impl_alt_from_data!($ty, __impl_altstring_methods, impl_inferbase_string, subset);
1156    };
1157    ($ty:ty, subset, serialize) => {
1158        $crate::__impl_alt_from_data!(
1159            $ty,
1160            __impl_altstring_methods,
1161            impl_inferbase_string,
1162            subset,
1163            serialize
1164        );
1165    };
1166    ($ty:ty, serialize, subset) => {
1167        $crate::impl_altstring_from_data!($ty, subset, serialize);
1168    };
1169}
1170
1171/// Internal macro for AltString method implementations.
1172#[macro_export]
1173#[doc(hidden)]
1174macro_rules! __impl_altstring_methods {
1175    ($ty:ty) => {
1176        impl $crate::altrep_traits::AltString for $ty {
1177            // String elt with lazy per-element caching in data2 STRSXP.
1178            //
1179            // On first access, allocates a STRSXP in data2 (initialized to R_NaString).
1180            // Each element is computed from Rust on first access and cached. Subsequent
1181            // accesses return the cached CHARSXP directly.
1182            //
1183            // For NA elements (Rust elt returns None), data2[i] stays R_NaString — we
1184            // re-probe Rust each time (O(1) index, returns None immediately). This is
1185            // simpler than a separate materialization bitmap and the cost is negligible.
1186            fn elt(x: $crate::ffi::SEXP, i: $crate::ffi::R_xlen_t) -> $crate::ffi::SEXP {
1187                unsafe {
1188                    let idx = i.max(0) as usize;
1189
1190                    // Get or allocate the data2 cache STRSXP
1191                    let mut data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2_raw(&x);
1192                    if data2.is_null()
1193                        || $crate::ffi::SexpExt::type_of(&data2) != $crate::ffi::SEXPTYPE::STRSXP
1194                    {
1195                        let n = <$ty as $crate::altrep_traits::Altrep>::length(x);
1196                        // Rf_allocVector(STRSXP, n) leaves elements UNINITIALIZED
1197                        // (garbage SEXP pointers). Must fill with R_NaString sentinel.
1198                        data2 = $crate::ffi::Rf_protect($crate::ffi::Rf_allocVector(
1199                            $crate::ffi::SEXPTYPE::STRSXP,
1200                            n,
1201                        ));
1202                        for j in 0..n {
1203                            $crate::ffi::SexpExt::set_string_elt(
1204                                &data2,
1205                                j,
1206                                $crate::ffi::SEXP::na_string(),
1207                            );
1208                        }
1209                        $crate::altrep_ext::AltrepSexpExt::set_altrep_data2(&x, data2);
1210                        $crate::ffi::Rf_unprotect(1);
1211                    }
1212
1213                    // Check cache: non-NA means already materialized
1214                    let cached = $crate::ffi::SexpExt::string_elt(&data2, i);
1215                    if cached != $crate::ffi::SEXP::na_string() {
1216                        return cached;
1217                    }
1218
1219                    // Cache miss (or genuine NA) — probe Rust source
1220                    let data = <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x);
1221                    match <$ty as $crate::altrep_data::AltStringData>::elt(data, idx) {
1222                        Some(s) => {
1223                            let charsxp = $crate::altrep_impl::checked_mkchar(s);
1224                            $crate::ffi::SexpExt::set_string_elt(&data2, i, charsxp);
1225                            charsxp
1226                        }
1227                        None => $crate::ffi::SEXP::na_string(),
1228                    }
1229                }
1230            }
1231
1232            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltStringData);
1233            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltStringData);
1234        }
1235    };
1236}
1237
1238/// Generate ALTREP trait implementations for a type that implements AltListData.
1239#[macro_export]
1240macro_rules! impl_altlist_from_data {
1241    ($ty:ty) => {
1242        $crate::impl_altlist_from_data!($ty, RUnwind);
1243    };
1244    ($ty:ty, $guard:ident) => {
1245        impl $crate::altrep_traits::Altrep for $ty {
1246            const GUARD: $crate::altrep_traits::AltrepGuard =
1247                $crate::altrep_traits::AltrepGuard::$guard;
1248
1249            fn length(x: $crate::ffi::SEXP) -> $crate::ffi::R_xlen_t {
1250                let data =
1251                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1252                <$ty as $crate::altrep_data::AltrepLen>::len(data) as $crate::ffi::R_xlen_t
1253            }
1254        }
1255
1256        impl $crate::altrep_traits::AltVec for $ty {}
1257
1258        impl $crate::altrep_traits::AltList for $ty {
1259            fn elt(x: $crate::ffi::SEXP, i: $crate::ffi::R_xlen_t) -> $crate::ffi::SEXP {
1260                let data =
1261                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1262                <$ty as $crate::altrep_data::AltListData>::elt(data, i.max(0) as usize)
1263            }
1264        }
1265
1266        $crate::impl_inferbase_list!($ty);
1267    };
1268}
1269
1270/// Internal macro: impl AltComplex methods (elt, get_region)
1271#[macro_export]
1272#[doc(hidden)]
1273macro_rules! __impl_altcomplex_methods {
1274    ($ty:ty) => {
1275        impl $crate::altrep_traits::AltComplex for $ty {
1276            $crate::__impl_alt_elt!(
1277                $ty,
1278                $crate::altrep_data::AltComplexData,
1279                $crate::ffi::Rcomplex,
1280                $crate::ffi::Rcomplex {
1281                    r: f64::NAN,
1282                    i: f64::NAN
1283                }
1284            );
1285            $crate::__impl_alt_get_region!(
1286                $ty,
1287                $crate::altrep_data::AltComplexData,
1288                $crate::ffi::Rcomplex
1289            );
1290        }
1291    };
1292}
1293
1294/// Generate ALTREP trait implementations for a type that implements AltComplexData.
1295///
1296/// Optional features can be enabled by passing additional arguments:
1297/// - `dataptr`: Enable `Dataptr` and `Dataptr_or_null` methods (requires `AltrepDataptr<Rcomplex>`)
1298/// - `serialize`: Enable serialization support (requires `AltrepSerialize`)
1299/// - `subset`: Enable optimized subsetting (requires `AltrepExtractSubset`)
1300#[macro_export]
1301macro_rules! impl_altcomplex_from_data {
1302    ($ty:ty) => {
1303        $crate::__impl_altrep_base!($ty);
1304        $crate::__impl_altvec_complex_dataptr!($ty);
1305        $crate::__impl_altcomplex_methods!($ty);
1306        $crate::impl_inferbase_complex!($ty);
1307    };
1308    ($ty:ty, dataptr) => {
1309        $crate::__impl_alt_from_data!(
1310            $ty,
1311            __impl_altcomplex_methods,
1312            impl_inferbase_complex,
1313            dataptr($crate::ffi::Rcomplex)
1314        );
1315    };
1316    ($ty:ty, serialize) => {
1317        $crate::__impl_altrep_base_with_serialize!($ty);
1318        $crate::__impl_altvec_complex_dataptr!($ty);
1319        $crate::__impl_altcomplex_methods!($ty);
1320        $crate::impl_inferbase_complex!($ty);
1321    };
1322    ($ty:ty, subset) => {
1323        $crate::__impl_alt_from_data!(
1324            $ty,
1325            __impl_altcomplex_methods,
1326            impl_inferbase_complex,
1327            subset
1328        );
1329    };
1330    ($ty:ty, dataptr, serialize) => {
1331        $crate::__impl_alt_from_data!(
1332            $ty,
1333            __impl_altcomplex_methods,
1334            impl_inferbase_complex,
1335            dataptr($crate::ffi::Rcomplex),
1336            serialize
1337        );
1338    };
1339    ($ty:ty, serialize, dataptr) => {
1340        $crate::impl_altcomplex_from_data!($ty, dataptr, serialize);
1341    };
1342    ($ty:ty, subset, serialize) => {
1343        $crate::__impl_alt_from_data!(
1344            $ty,
1345            __impl_altcomplex_methods,
1346            impl_inferbase_complex,
1347            subset,
1348            serialize
1349        );
1350    };
1351    ($ty:ty, serialize, subset) => {
1352        $crate::impl_altcomplex_from_data!($ty, subset, serialize);
1353    };
1354}
1355// endregion
1356
1357// region: Meta-macros for built-in ALTREP family instantiation
1358//
1359// `impl_builtin_altrep_family!` is a single declarative meta-macro that maps a
1360// (type, family, dataptr-mode, sym) 4-tuple to the correct per-family
1361// `impl_alt*_from_data!` call AND emits a linkme `MX_ALTREP_REGISTRATIONS` entry
1362// so the class is registered at `R_init_*` time (no hand-enumerated list needed).
1363//
1364// The `$sym:ident` parameter is a unique snake_case identifier used as the suffix
1365// for the `#[no_mangle]` registration fn and the linkme static.  It must be unique
1366// across all call sites — naming convention: `<container>_<elem>` (e.g. `Vec_i32`,
1367// `Box_bool`, `Cow_str`, `Range_i32`).
1368//
1369// ## Families
1370//
1371// | Token     | Delegates to               |
1372// |-----------|----------------------------|
1373// | `integer` | `impl_altinteger_from_data!` |
1374// | `real`    | `impl_altreal_from_data!`    |
1375// | `logical` | `impl_altlogical_from_data!` |
1376// | `raw`     | `impl_altraw_from_data!`     |
1377// | `string`  | `impl_altstring_from_data!`  |
1378// | `complex` | `impl_altcomplex_from_data!` |
1379//
1380// ## Dataptr modes
1381//
1382// | Token         | Meaning                                                     |
1383// |---------------|-------------------------------------------------------------|
1384// | `dataptr`     | Type has a direct contiguous pointer (`RNativeType` backed) |
1385// | `materializing` | No direct pointer; materializes into data2 on first access  |
1386//
1387// The `materializing` arm expands to `materializing_dataptr, serialize` in the
1388// underlying macro (bool→i32 conversion, Range compute-on-access, etc.).
1389// Both arms preserve `const GUARD = AltrepGuard::RUnwind` — the default from
1390// `__impl_altrep_base!` and `__impl_altrep_base_with_serialize!`.
1391//
1392// ## Corner cases NOT handled by this macro
1393//
1394// - `[T; N]` const-generic arrays: use const generics (`impl<const N>`); left in the
1395//   "Array implementations" region below.
1396// - `&'static [T]` static slices: unique lifetime + writable-assert pattern; left in
1397//   the "Static slice implementations" region below.
1398
1399/// Generate ALTREP trait impls AND a linkme `MX_ALTREP_REGISTRATIONS` entry for a
1400/// builtin type.
1401///
1402/// ## Arguments
1403///
1404/// - `$ty:ty` — the builtin container type (e.g. `Vec<i32>`, `Box<[f64]>`)
1405/// - `$family:ident` — ALTREP family token: `integer`, `real`, `logical`, `raw`,
1406///   `string`, or `complex`
1407/// - `$mode:ident` — dataptr mode: `dataptr` or `materializing`
1408/// - `$reg_fn:ident` — unique `#[no_mangle]` name for the registration fn
1409///   (convention: `__mx_altrep_reg_builtin_<sym>`)
1410/// - `$entry_ident:ident` — unique name for the linkme static
1411///   (convention: `__MX_ALTREP_REG_ENTRY_builtin_<sym>`)
1412///
1413/// Both identifier arguments must be globally unique across all call sites.
1414/// The registration fn is always emitted; the `#[distributed_slice]` attribute
1415/// is guarded by `cfg_attr(not(target_arch = "wasm32"), ...)` so linkme's
1416/// compile-error arm is not reached on wasm32 targets.
1417#[doc(hidden)]
1418macro_rules! impl_builtin_altrep_family {
1419    // dataptr arm — type has a direct contiguous native pointer
1420    ($ty:ty, integer, dataptr, $reg_fn:ident, $entry_ident:ident) => {
1421        $crate::impl_altinteger_from_data!($ty, dataptr, serialize);
1422        #[doc(hidden)]
1423        #[unsafe(no_mangle)]
1424        pub extern "C" fn $reg_fn() {
1425            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1426        }
1427        #[cfg_attr(
1428                    not(target_arch = "wasm32"),
1429                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1430                    linkme(crate = $crate::linkme)
1431                )]
1432        #[doc(hidden)]
1433        #[allow(non_upper_case_globals)]
1434        static $entry_ident: $crate::registry::AltrepRegistration =
1435            $crate::registry::AltrepRegistration {
1436                register: $reg_fn,
1437                symbol: stringify!($reg_fn),
1438            };
1439    };
1440    ($ty:ty, real, dataptr, $reg_fn:ident, $entry_ident:ident) => {
1441        $crate::impl_altreal_from_data!($ty, dataptr, serialize);
1442        #[doc(hidden)]
1443        #[unsafe(no_mangle)]
1444        pub extern "C" fn $reg_fn() {
1445            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1446        }
1447        #[cfg_attr(
1448                    not(target_arch = "wasm32"),
1449                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1450                    linkme(crate = $crate::linkme)
1451                )]
1452        #[doc(hidden)]
1453        #[allow(non_upper_case_globals)]
1454        static $entry_ident: $crate::registry::AltrepRegistration =
1455            $crate::registry::AltrepRegistration {
1456                register: $reg_fn,
1457                symbol: stringify!($reg_fn),
1458            };
1459    };
1460    ($ty:ty, logical, dataptr, $reg_fn:ident, $entry_ident:ident) => {
1461        $crate::impl_altlogical_from_data!($ty, dataptr, serialize);
1462        #[doc(hidden)]
1463        #[unsafe(no_mangle)]
1464        pub extern "C" fn $reg_fn() {
1465            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1466        }
1467        #[cfg_attr(
1468                    not(target_arch = "wasm32"),
1469                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1470                    linkme(crate = $crate::linkme)
1471                )]
1472        #[doc(hidden)]
1473        #[allow(non_upper_case_globals)]
1474        static $entry_ident: $crate::registry::AltrepRegistration =
1475            $crate::registry::AltrepRegistration {
1476                register: $reg_fn,
1477                symbol: stringify!($reg_fn),
1478            };
1479    };
1480    ($ty:ty, raw, dataptr, $reg_fn:ident, $entry_ident:ident) => {
1481        $crate::impl_altraw_from_data!($ty, dataptr, serialize);
1482        #[doc(hidden)]
1483        #[unsafe(no_mangle)]
1484        pub extern "C" fn $reg_fn() {
1485            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1486        }
1487        #[cfg_attr(
1488                    not(target_arch = "wasm32"),
1489                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1490                    linkme(crate = $crate::linkme)
1491                )]
1492        #[doc(hidden)]
1493        #[allow(non_upper_case_globals)]
1494        static $entry_ident: $crate::registry::AltrepRegistration =
1495            $crate::registry::AltrepRegistration {
1496                register: $reg_fn,
1497                symbol: stringify!($reg_fn),
1498            };
1499    };
1500    ($ty:ty, string, dataptr, $reg_fn:ident, $entry_ident:ident) => {
1501        $crate::impl_altstring_from_data!($ty, dataptr, serialize);
1502        #[doc(hidden)]
1503        #[unsafe(no_mangle)]
1504        pub extern "C" fn $reg_fn() {
1505            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1506        }
1507        #[cfg_attr(
1508                    not(target_arch = "wasm32"),
1509                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1510                    linkme(crate = $crate::linkme)
1511                )]
1512        #[doc(hidden)]
1513        #[allow(non_upper_case_globals)]
1514        static $entry_ident: $crate::registry::AltrepRegistration =
1515            $crate::registry::AltrepRegistration {
1516                register: $reg_fn,
1517                symbol: stringify!($reg_fn),
1518            };
1519    };
1520    ($ty:ty, complex, dataptr, $reg_fn:ident, $entry_ident:ident) => {
1521        $crate::impl_altcomplex_from_data!($ty, dataptr, serialize);
1522        #[doc(hidden)]
1523        #[unsafe(no_mangle)]
1524        pub extern "C" fn $reg_fn() {
1525            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1526        }
1527        #[cfg_attr(
1528                    not(target_arch = "wasm32"),
1529                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1530                    linkme(crate = $crate::linkme)
1531                )]
1532        #[doc(hidden)]
1533        #[allow(non_upper_case_globals)]
1534        static $entry_ident: $crate::registry::AltrepRegistration =
1535            $crate::registry::AltrepRegistration {
1536                register: $reg_fn,
1537                symbol: stringify!($reg_fn),
1538            };
1539    };
1540    // materializing arm — no direct native pointer; materializes on DATAPTR access.
1541    // Used for: bool (bool→i32 via LGLSXP), Range<T> (compute-on-access).
1542    // Guard remains RUnwind (the default) — the underlying macros do not change it.
1543    ($ty:ty, integer, materializing, $reg_fn:ident, $entry_ident:ident) => {
1544        $crate::impl_altinteger_from_data!($ty, materializing_dataptr, serialize);
1545        #[doc(hidden)]
1546        #[unsafe(no_mangle)]
1547        pub extern "C" fn $reg_fn() {
1548            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1549        }
1550        #[cfg_attr(
1551                    not(target_arch = "wasm32"),
1552                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1553                    linkme(crate = $crate::linkme)
1554                )]
1555        #[doc(hidden)]
1556        #[allow(non_upper_case_globals)]
1557        static $entry_ident: $crate::registry::AltrepRegistration =
1558            $crate::registry::AltrepRegistration {
1559                register: $reg_fn,
1560                symbol: stringify!($reg_fn),
1561            };
1562    };
1563    ($ty:ty, real, materializing, $reg_fn:ident, $entry_ident:ident) => {
1564        $crate::impl_altreal_from_data!($ty, materializing_dataptr, serialize);
1565        #[doc(hidden)]
1566        #[unsafe(no_mangle)]
1567        pub extern "C" fn $reg_fn() {
1568            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1569        }
1570        #[cfg_attr(
1571                    not(target_arch = "wasm32"),
1572                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1573                    linkme(crate = $crate::linkme)
1574                )]
1575        #[doc(hidden)]
1576        #[allow(non_upper_case_globals)]
1577        static $entry_ident: $crate::registry::AltrepRegistration =
1578            $crate::registry::AltrepRegistration {
1579                register: $reg_fn,
1580                symbol: stringify!($reg_fn),
1581            };
1582    };
1583    ($ty:ty, logical, materializing, $reg_fn:ident, $entry_ident:ident) => {
1584        $crate::impl_altlogical_from_data!($ty, materializing_dataptr, serialize);
1585        #[doc(hidden)]
1586        #[unsafe(no_mangle)]
1587        pub extern "C" fn $reg_fn() {
1588            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1589        }
1590        #[cfg_attr(
1591                    not(target_arch = "wasm32"),
1592                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1593                    linkme(crate = $crate::linkme)
1594                )]
1595        #[doc(hidden)]
1596        #[allow(non_upper_case_globals)]
1597        static $entry_ident: $crate::registry::AltrepRegistration =
1598            $crate::registry::AltrepRegistration {
1599                register: $reg_fn,
1600                symbol: stringify!($reg_fn),
1601            };
1602    };
1603    ($ty:ty, raw, materializing, $reg_fn:ident, $entry_ident:ident) => {
1604        $crate::impl_altraw_from_data!($ty, materializing_dataptr, serialize);
1605        #[doc(hidden)]
1606        #[unsafe(no_mangle)]
1607        pub extern "C" fn $reg_fn() {
1608            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1609        }
1610        #[cfg_attr(
1611                    not(target_arch = "wasm32"),
1612                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1613                    linkme(crate = $crate::linkme)
1614                )]
1615        #[doc(hidden)]
1616        #[allow(non_upper_case_globals)]
1617        static $entry_ident: $crate::registry::AltrepRegistration =
1618            $crate::registry::AltrepRegistration {
1619                register: $reg_fn,
1620                symbol: stringify!($reg_fn),
1621            };
1622    };
1623    ($ty:ty, string, materializing, $reg_fn:ident, $entry_ident:ident) => {
1624        $crate::impl_altstring_from_data!($ty, materializing_dataptr, serialize);
1625        #[doc(hidden)]
1626        #[unsafe(no_mangle)]
1627        pub extern "C" fn $reg_fn() {
1628            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1629        }
1630        #[cfg_attr(
1631                    not(target_arch = "wasm32"),
1632                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1633                    linkme(crate = $crate::linkme)
1634                )]
1635        #[doc(hidden)]
1636        #[allow(non_upper_case_globals)]
1637        static $entry_ident: $crate::registry::AltrepRegistration =
1638            $crate::registry::AltrepRegistration {
1639                register: $reg_fn,
1640                symbol: stringify!($reg_fn),
1641            };
1642    };
1643    ($ty:ty, complex, materializing, $reg_fn:ident, $entry_ident:ident) => {
1644        $crate::impl_altcomplex_from_data!($ty, materializing_dataptr, serialize);
1645        #[doc(hidden)]
1646        #[unsafe(no_mangle)]
1647        pub extern "C" fn $reg_fn() {
1648            <$ty as $crate::altrep::RegisterAltrep>::get_or_init_class();
1649        }
1650        #[cfg_attr(
1651                    not(target_arch = "wasm32"),
1652                    $crate::linkme::distributed_slice($crate::registry::MX_ALTREP_REGISTRATIONS),
1653                    linkme(crate = $crate::linkme)
1654                )]
1655        #[doc(hidden)]
1656        #[allow(non_upper_case_globals)]
1657        static $entry_ident: $crate::registry::AltrepRegistration =
1658            $crate::registry::AltrepRegistration {
1659                register: $reg_fn,
1660                symbol: stringify!($reg_fn),
1661            };
1662    };
1663}
1664
1665// region: Built-in implementations for standard types
1666// These implementations are provided here to satisfy the orphan rules.
1667// User crates can use these types directly with delegate_data.
1668//
1669// All types implement AltrepSerialize; serialize is injected by the meta-macro.
1670// Guard: RUnwind (default from __impl_altrep_base[_with_serialize]! — not overridden).
1671
1672// Vec<T> — owned, heap-allocated contiguous storage.
1673// bool uses `materializing` because bool is not RNativeType (R stores logicals as i32).
1674impl_builtin_altrep_family!(
1675    Vec<i32>,
1676    integer,
1677    dataptr,
1678    __mx_altrep_reg_builtin_Vec_i32,
1679    __MX_ALTREP_REG_ENTRY_builtin_Vec_i32
1680);
1681impl_builtin_altrep_family!(
1682    Vec<f64>,
1683    real,
1684    dataptr,
1685    __mx_altrep_reg_builtin_Vec_f64,
1686    __MX_ALTREP_REG_ENTRY_builtin_Vec_f64
1687);
1688impl_builtin_altrep_family!(
1689    Vec<bool>,
1690    logical,
1691    materializing,
1692    __mx_altrep_reg_builtin_Vec_bool,
1693    __MX_ALTREP_REG_ENTRY_builtin_Vec_bool
1694);
1695impl_builtin_altrep_family!(
1696    Vec<u8>,
1697    raw,
1698    dataptr,
1699    __mx_altrep_reg_builtin_Vec_u8,
1700    __MX_ALTREP_REG_ENTRY_builtin_Vec_u8
1701);
1702impl_builtin_altrep_family!(
1703    Vec<String>,
1704    string,
1705    dataptr,
1706    __mx_altrep_reg_builtin_Vec_String,
1707    __MX_ALTREP_REG_ENTRY_builtin_Vec_String
1708);
1709impl_builtin_altrep_family!(
1710    Vec<Option<String>>,
1711    string,
1712    dataptr,
1713    __mx_altrep_reg_builtin_Vec_Option_String,
1714    __MX_ALTREP_REG_ENTRY_builtin_Vec_Option_String
1715);
1716// Cow string vectors — zero-copy from R, ALTREP output without copying back.
1717// Serialize: Rf_mkCharLenCE hits R's CHARSXP cache (no string data copy for borrowed).
1718// Unserialize: TryFromSexp uses charsxp_to_cow (zero-copy borrow for UTF-8).
1719impl_builtin_altrep_family!(
1720    Vec<std::borrow::Cow<'static, str>>,
1721    string,
1722    dataptr,
1723    __mx_altrep_reg_builtin_Vec_Cow_str,
1724    __MX_ALTREP_REG_ENTRY_builtin_Vec_Cow_str
1725);
1726impl_builtin_altrep_family!(
1727    Vec<Option<std::borrow::Cow<'static, str>>>,
1728    string,
1729    dataptr,
1730    __mx_altrep_reg_builtin_Vec_Option_Cow_str,
1731    __MX_ALTREP_REG_ENTRY_builtin_Vec_Option_Cow_str
1732);
1733impl_builtin_altrep_family!(
1734    Vec<crate::ffi::Rcomplex>,
1735    complex,
1736    dataptr,
1737    __mx_altrep_reg_builtin_Vec_Rcomplex,
1738    __MX_ALTREP_REG_ENTRY_builtin_Vec_Rcomplex
1739);
1740
1741// Range<T> — compute-on-access (no direct pointer); materializes into data2 INTSXP/REALSXP.
1742impl_builtin_altrep_family!(
1743    std::ops::Range<i32>,
1744    integer,
1745    materializing,
1746    __mx_altrep_reg_builtin_Range_i32,
1747    __MX_ALTREP_REG_ENTRY_builtin_Range_i32
1748);
1749impl_builtin_altrep_family!(
1750    std::ops::Range<i64>,
1751    integer,
1752    materializing,
1753    __mx_altrep_reg_builtin_Range_i64,
1754    __MX_ALTREP_REG_ENTRY_builtin_Range_i64
1755);
1756impl_builtin_altrep_family!(
1757    std::ops::Range<f64>,
1758    real,
1759    materializing,
1760    __mx_altrep_reg_builtin_Range_f64,
1761    __MX_ALTREP_REG_ENTRY_builtin_Range_f64
1762);
1763// endregion
1764
1765// region: Box<[T]> implementations
1766// Box<[T]> is a fat pointer (Sized) that wraps a DST slice.
1767// Unlike Vec<T>, it has no capacity field - just ptr + len (2 words).
1768// Useful for fixed-size heap allocations.
1769// bool uses `materializing` (same reason as Vec<bool>).
1770
1771impl_builtin_altrep_family!(
1772    Box<[i32]>,
1773    integer,
1774    dataptr,
1775    __mx_altrep_reg_builtin_Box_i32,
1776    __MX_ALTREP_REG_ENTRY_builtin_Box_i32
1777);
1778impl_builtin_altrep_family!(
1779    Box<[f64]>,
1780    real,
1781    dataptr,
1782    __mx_altrep_reg_builtin_Box_f64,
1783    __MX_ALTREP_REG_ENTRY_builtin_Box_f64
1784);
1785impl_builtin_altrep_family!(
1786    Box<[bool]>,
1787    logical,
1788    materializing,
1789    __mx_altrep_reg_builtin_Box_bool,
1790    __MX_ALTREP_REG_ENTRY_builtin_Box_bool
1791);
1792impl_builtin_altrep_family!(
1793    Box<[u8]>,
1794    raw,
1795    dataptr,
1796    __mx_altrep_reg_builtin_Box_u8,
1797    __MX_ALTREP_REG_ENTRY_builtin_Box_u8
1798);
1799impl_builtin_altrep_family!(
1800    Box<[String]>,
1801    string,
1802    dataptr,
1803    __mx_altrep_reg_builtin_Box_String,
1804    __MX_ALTREP_REG_ENTRY_builtin_Box_String
1805);
1806impl_builtin_altrep_family!(
1807    Box<[crate::ffi::Rcomplex]>,
1808    complex,
1809    dataptr,
1810    __mx_altrep_reg_builtin_Box_Rcomplex,
1811    __MX_ALTREP_REG_ENTRY_builtin_Box_Rcomplex
1812);
1813
1814// Cow<'static, [T]> — zero-copy borrow from R with copy-on-write dataptr.
1815// Borrowed variants expose R's data directly; Owned behaves like Vec.
1816impl_builtin_altrep_family!(
1817    std::borrow::Cow<'static, [i32]>,
1818    integer,
1819    dataptr,
1820    __mx_altrep_reg_builtin_Cow_i32,
1821    __MX_ALTREP_REG_ENTRY_builtin_Cow_i32
1822);
1823impl_builtin_altrep_family!(
1824    std::borrow::Cow<'static, [f64]>,
1825    real,
1826    dataptr,
1827    __mx_altrep_reg_builtin_Cow_f64,
1828    __MX_ALTREP_REG_ENTRY_builtin_Cow_f64
1829);
1830impl_builtin_altrep_family!(
1831    std::borrow::Cow<'static, [u8]>,
1832    raw,
1833    dataptr,
1834    __mx_altrep_reg_builtin_Cow_u8,
1835    __MX_ALTREP_REG_ENTRY_builtin_Cow_u8
1836);
1837impl_builtin_altrep_family!(
1838    std::borrow::Cow<'static, [crate::ffi::Rcomplex]>,
1839    complex,
1840    dataptr,
1841    __mx_altrep_reg_builtin_Cow_Rcomplex,
1842    __MX_ALTREP_REG_ENTRY_builtin_Cow_Rcomplex
1843);
1844
1845// endregion
1846
1847// region: Array implementations (const generics)
1848//
1849// Macro-generated for numeric families (i32, f64, u8, Rcomplex) that share
1850// the same pattern: Altrep + AltVec with dataptr + family trait + InferBase.
1851// Bool and String arrays are hand-written because they differ structurally
1852// (bool has no direct dataptr; String elt returns SEXP).
1853
1854/// Generate all ALTREP trait impls + InferBase for a numeric [T; N] array family.
1855/// Pass optional extra items via `extra { ... }` to include in the family trait impl.
1856macro_rules! impl_altrep_array_numeric {
1857    (
1858        elem = $elem:ty,
1859        data_trait = $data_trait:path,
1860        alt_trait = $alt_trait:path,
1861        rbase = $rbase:expr,
1862        make_class_fn = $make_class_fn:path,
1863        install_family_fn = $install_family_fn:ident
1864        $(, extra { $($extra:tt)* } )?
1865        $(,)?
1866    ) => {
1867        impl<const N: usize> crate::altrep_traits::Altrep for [$elem; N] {
1868            fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1869                let data = unsafe {
1870                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1871                };
1872                crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
1873            }
1874        }
1875
1876        impl<const N: usize> crate::altrep_traits::AltVec for [$elem; N] {
1877            const HAS_DATAPTR: bool = true;
1878
1879            fn dataptr(x: crate::ffi::SEXP, _writable: bool) -> *mut core::ffi::c_void {
1880                let data = unsafe {
1881                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
1882                };
1883                data.as_mut_ptr().cast::<core::ffi::c_void>()
1884            }
1885        }
1886
1887        impl<const N: usize> $alt_trait for [$elem; N] {
1888            const HAS_ELT: bool = true;
1889
1890            fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> $elem {
1891                let data = unsafe {
1892                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1893                };
1894                <[$elem; N] as $data_trait>::elt(data, i.max(0) as usize)
1895            }
1896
1897            const HAS_GET_REGION: bool = true;
1898
1899            fn get_region(
1900                x: crate::ffi::SEXP,
1901                start: crate::ffi::R_xlen_t,
1902                len: crate::ffi::R_xlen_t,
1903                buf: &mut [$elem],
1904            ) -> crate::ffi::R_xlen_t {
1905                if start < 0 || len <= 0 {
1906                    return 0;
1907                }
1908                let data = unsafe {
1909                    <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
1910                };
1911                <[$elem; N] as $data_trait>::get_region(data, start as usize, len as usize, buf)
1912                    as crate::ffi::R_xlen_t
1913            }
1914
1915            $($($extra)*)?
1916        }
1917
1918        impl<const N: usize> crate::altrep_data::InferBase for [$elem; N] {
1919            const BASE: crate::altrep::RBase = $rbase;
1920
1921            unsafe fn make_class(
1922                class_name: *const i8,
1923                pkg_name: *const i8,
1924            ) -> crate::ffi::altrep::R_altrep_class_t {
1925                let cls = unsafe { $make_class_fn(class_name, pkg_name, crate::altrep_dll_info()) };
1926                let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
1927                crate::altrep::validate_altrep_class(cls, name, Self::BASE)
1928            }
1929
1930            unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
1931                unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
1932                unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
1933                unsafe { crate::altrep_bridge::$install_family_fn::<Self>(cls) };
1934            }
1935        }
1936    };
1937}
1938
1939/// no_na fragment for families that support it (Integer, Real).
1940macro_rules! altrep_array_no_na {
1941    ($elem:ty, $data_trait:path) => {
1942        const HAS_NO_NA: bool = true;
1943
1944        fn no_na(x: crate::ffi::SEXP) -> i32 {
1945            let data =
1946                unsafe { <[$elem; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1947            <[$elem; N] as $data_trait>::no_na(data)
1948                .map(i32::from)
1949                .unwrap_or(0)
1950        }
1951    };
1952}
1953
1954impl_altrep_array_numeric!(
1955    elem = i32,
1956    data_trait = crate::altrep_data::AltIntegerData,
1957    alt_trait = crate::altrep_traits::AltInteger,
1958    rbase = crate::altrep::RBase::Int,
1959    make_class_fn = crate::ffi::altrep::R_make_altinteger_class,
1960    install_family_fn = install_int,
1961    extra { altrep_array_no_na!(i32, crate::altrep_data::AltIntegerData); },
1962);
1963impl_altrep_array_numeric!(
1964    elem = f64,
1965    data_trait = crate::altrep_data::AltRealData,
1966    alt_trait = crate::altrep_traits::AltReal,
1967    rbase = crate::altrep::RBase::Real,
1968    make_class_fn = crate::ffi::altrep::R_make_altreal_class,
1969    install_family_fn = install_real,
1970    extra { altrep_array_no_na!(f64, crate::altrep_data::AltRealData); },
1971);
1972impl_altrep_array_numeric!(
1973    elem = u8,
1974    data_trait = crate::altrep_data::AltRawData,
1975    alt_trait = crate::altrep_traits::AltRaw,
1976    rbase = crate::altrep::RBase::Raw,
1977    make_class_fn = crate::ffi::altrep::R_make_altraw_class,
1978    install_family_fn = install_raw,
1979);
1980impl_altrep_array_numeric!(
1981    elem = crate::ffi::Rcomplex,
1982    data_trait = crate::altrep_data::AltComplexData,
1983    alt_trait = crate::altrep_traits::AltComplex,
1984    rbase = crate::altrep::RBase::Complex,
1985    make_class_fn = crate::ffi::altrep::R_make_altcomplex_class,
1986    install_family_fn = install_cplx,
1987);
1988
1989// Logical arrays — bool != i32, no direct dataptr, elt returns i32 via to_r_int()
1990impl<const N: usize> crate::altrep_traits::Altrep for [bool; N] {
1991    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
1992        let data =
1993            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1994        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
1995    }
1996}
1997
1998impl<const N: usize> crate::altrep_traits::AltVec for [bool; N] {}
1999
2000impl<const N: usize> crate::altrep_traits::AltLogical for [bool; N] {
2001    const HAS_ELT: bool = true;
2002
2003    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> i32 {
2004        let data =
2005            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2006        <[bool; N] as crate::altrep_data::AltLogicalData>::elt(data, i.max(0) as usize).to_r_int()
2007    }
2008
2009    const HAS_NO_NA: bool = true;
2010
2011    fn no_na(x: crate::ffi::SEXP) -> i32 {
2012        let data =
2013            unsafe { <[bool; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2014        <[bool; N] as crate::altrep_data::AltLogicalData>::no_na(data)
2015            .map(i32::from)
2016            .unwrap_or(0)
2017    }
2018}
2019
2020impl<const N: usize> crate::altrep_data::InferBase for [bool; N] {
2021    const BASE: crate::altrep::RBase = crate::altrep::RBase::Logical;
2022
2023    unsafe fn make_class(
2024        class_name: *const i8,
2025        pkg_name: *const i8,
2026    ) -> crate::ffi::altrep::R_altrep_class_t {
2027        let cls = unsafe {
2028            crate::ffi::altrep::R_make_altlogical_class(
2029                class_name,
2030                pkg_name,
2031                crate::altrep_dll_info(),
2032            )
2033        };
2034        let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
2035        crate::altrep::validate_altrep_class(cls, name, Self::BASE)
2036    }
2037
2038    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
2039        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
2040        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
2041        unsafe { crate::altrep_bridge::install_lgl::<Self>(cls) };
2042    }
2043}
2044
2045// String arrays — no dataptr, elt returns SEXP via checked_mkchar
2046impl<const N: usize> crate::altrep_traits::Altrep for [String; N] {
2047    const GUARD: crate::altrep_traits::AltrepGuard = crate::altrep_traits::AltrepGuard::RUnwind;
2048
2049    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2050        let data =
2051            unsafe { <[String; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2052        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2053    }
2054}
2055
2056impl<const N: usize> crate::altrep_traits::AltVec for [String; N] {}
2057
2058impl<const N: usize> crate::altrep_traits::AltString for [String; N] {
2059    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::SEXP {
2060        let data =
2061            unsafe { <[String; N] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2062        match <[String; N] as crate::altrep_data::AltStringData>::elt(data, i.max(0) as usize) {
2063            Some(s) => unsafe { checked_mkchar(s) },
2064            None => crate::ffi::SEXP::na_string(),
2065        }
2066    }
2067}
2068
2069impl<const N: usize> crate::altrep_data::InferBase for [String; N] {
2070    const BASE: crate::altrep::RBase = crate::altrep::RBase::String;
2071
2072    unsafe fn make_class(
2073        class_name: *const i8,
2074        pkg_name: *const i8,
2075    ) -> crate::ffi::altrep::R_altrep_class_t {
2076        let cls = unsafe {
2077            crate::ffi::altrep::R_make_altstring_class(
2078                class_name,
2079                pkg_name,
2080                crate::altrep_dll_info(),
2081            )
2082        };
2083        let name = unsafe { core::ffi::CStr::from_ptr(class_name) };
2084        crate::altrep::validate_altrep_class(cls, name, Self::BASE)
2085    }
2086
2087    unsafe fn install_methods(cls: crate::ffi::altrep::R_altrep_class_t) {
2088        unsafe { crate::altrep_bridge::install_base::<Self>(cls) };
2089        unsafe { crate::altrep_bridge::install_vec::<Self>(cls) };
2090        unsafe { crate::altrep_bridge::install_str::<Self>(cls) };
2091    }
2092}
2093// endregion
2094
2095// region: Static slice implementations (&'static [T])
2096//
2097// `&'static [T]` is Sized (fat pointer: ptr + len) and satisfies 'static,
2098// so it can be used DIRECTLY with ALTREP via ExternalPtr.
2099//
2100// Use cases:
2101// - Const arrays: `static DATA: [i32; 5] = [1, 2, 3, 4, 5]; create_altrep(&DATA[..])`
2102// - Leaked data: `let s: &'static [i32] = Box::leak(vec.into_boxed_slice());`
2103// - Memory-mapped files with 'static lifetime
2104
2105// Integer static slices
2106impl crate::altrep_traits::Altrep for &'static [i32] {
2107    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2108        let data =
2109            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2110        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2111    }
2112}
2113
2114impl crate::altrep_traits::AltVec for &'static [i32] {
2115    const HAS_DATAPTR: bool = true;
2116
2117    fn dataptr(x: crate::ffi::SEXP, writable: bool) -> *mut std::ffi::c_void {
2118        // Static data cannot be modified. Panic is caught by RUnwind guard.
2119        assert!(
2120            !writable,
2121            "cannot get writable DATAPTR for static ALTREP data"
2122        );
2123        let data =
2124            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2125        data.as_ptr().cast::<std::ffi::c_void>().cast_mut()
2126    }
2127
2128    const HAS_DATAPTR_OR_NULL: bool = true;
2129
2130    fn dataptr_or_null(x: crate::ffi::SEXP) -> *const std::ffi::c_void {
2131        let data =
2132            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2133        data.as_ptr().cast::<std::ffi::c_void>()
2134    }
2135}
2136
2137impl crate::altrep_traits::AltInteger for &'static [i32] {
2138    const HAS_ELT: bool = true;
2139
2140    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> i32 {
2141        let data =
2142            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2143        crate::altrep_data::AltIntegerData::elt(data, i.max(0) as usize)
2144    }
2145
2146    const HAS_GET_REGION: bool = true;
2147
2148    fn get_region(
2149        x: crate::ffi::SEXP,
2150        start: crate::ffi::R_xlen_t,
2151        len: crate::ffi::R_xlen_t,
2152        buf: &mut [i32],
2153    ) -> crate::ffi::R_xlen_t {
2154        if start < 0 || len <= 0 {
2155            return 0;
2156        }
2157        let data =
2158            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2159        let len = len as usize;
2160        crate::altrep_data::AltIntegerData::get_region(data, start as usize, len, buf)
2161            as crate::ffi::R_xlen_t
2162    }
2163
2164    const HAS_NO_NA: bool = true;
2165
2166    fn no_na(x: crate::ffi::SEXP) -> i32 {
2167        let data =
2168            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2169        crate::altrep_data::AltIntegerData::no_na(data)
2170            .map(|b| if b { 1 } else { 0 })
2171            .unwrap_or(0)
2172    }
2173
2174    const HAS_SUM: bool = true;
2175
2176    // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
2177    fn sum(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
2178        let data =
2179            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2180        crate::altrep_data::AltIntegerData::sum(data, narm)
2181            .map(|s| {
2182                if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
2183                    crate::ffi::SEXP::scalar_integer(s as i32)
2184                } else {
2185                    crate::ffi::SEXP::scalar_real(s as f64)
2186                }
2187            })
2188            .unwrap_or(crate::ffi::SEXP::null())
2189    }
2190
2191    const HAS_MIN: bool = true;
2192
2193    fn min(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
2194        let data =
2195            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2196        crate::altrep_data::AltIntegerData::min(data, narm)
2197            .map(crate::ffi::SEXP::scalar_integer)
2198            .unwrap_or(crate::ffi::SEXP::null())
2199    }
2200
2201    const HAS_MAX: bool = true;
2202
2203    fn max(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
2204        let data =
2205            unsafe { <&'static [i32] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2206        crate::altrep_data::AltIntegerData::max(data, narm)
2207            .map(crate::ffi::SEXP::scalar_integer)
2208            .unwrap_or(crate::ffi::SEXP::null())
2209    }
2210}
2211
2212crate::impl_inferbase_integer!(&'static [i32]);
2213
2214// Real static slices
2215impl crate::altrep_traits::Altrep for &'static [f64] {
2216    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2217        let data =
2218            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2219        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2220    }
2221}
2222
2223impl crate::altrep_traits::AltVec for &'static [f64] {
2224    const HAS_DATAPTR: bool = true;
2225
2226    fn dataptr(x: crate::ffi::SEXP, writable: bool) -> *mut std::ffi::c_void {
2227        assert!(
2228            !writable,
2229            "cannot get writable DATAPTR for static ALTREP data"
2230        );
2231        let data =
2232            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2233        data.as_ptr().cast::<std::ffi::c_void>().cast_mut()
2234    }
2235
2236    const HAS_DATAPTR_OR_NULL: bool = true;
2237
2238    fn dataptr_or_null(x: crate::ffi::SEXP) -> *const std::ffi::c_void {
2239        let data =
2240            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2241        data.as_ptr().cast::<std::ffi::c_void>()
2242    }
2243}
2244
2245impl crate::altrep_traits::AltReal for &'static [f64] {
2246    const HAS_ELT: bool = true;
2247
2248    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> f64 {
2249        let data =
2250            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2251        crate::altrep_data::AltRealData::elt(data, i.max(0) as usize)
2252    }
2253
2254    const HAS_GET_REGION: bool = true;
2255
2256    fn get_region(
2257        x: crate::ffi::SEXP,
2258        start: crate::ffi::R_xlen_t,
2259        len: crate::ffi::R_xlen_t,
2260        buf: &mut [f64],
2261    ) -> crate::ffi::R_xlen_t {
2262        if start < 0 || len <= 0 {
2263            return 0;
2264        }
2265        let data =
2266            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2267        let len = len as usize;
2268        crate::altrep_data::AltRealData::get_region(data, start as usize, len, buf)
2269            as crate::ffi::R_xlen_t
2270    }
2271
2272    const HAS_NO_NA: bool = true;
2273
2274    fn no_na(x: crate::ffi::SEXP) -> i32 {
2275        let data =
2276            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2277        crate::altrep_data::AltRealData::no_na(data)
2278            .map(|b| if b { 1 } else { 0 })
2279            .unwrap_or(0)
2280    }
2281
2282    const HAS_SUM: bool = true;
2283
2284    // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
2285    fn sum(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
2286        let data =
2287            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2288        crate::altrep_data::AltRealData::sum(data, narm)
2289            .map(crate::ffi::SEXP::scalar_real)
2290            .unwrap_or(crate::ffi::SEXP::null())
2291    }
2292
2293    const HAS_MIN: bool = true;
2294
2295    fn min(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
2296        let data =
2297            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2298        crate::altrep_data::AltRealData::min(data, narm)
2299            .map(crate::ffi::SEXP::scalar_real)
2300            .unwrap_or(crate::ffi::SEXP::null())
2301    }
2302
2303    const HAS_MAX: bool = true;
2304
2305    fn max(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
2306        let data =
2307            unsafe { <&'static [f64] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2308        crate::altrep_data::AltRealData::max(data, narm)
2309            .map(crate::ffi::SEXP::scalar_real)
2310            .unwrap_or(crate::ffi::SEXP::null())
2311    }
2312}
2313
2314crate::impl_inferbase_real!(&'static [f64]);
2315
2316// Logical static slices
2317impl crate::altrep_traits::Altrep for &'static [bool] {
2318    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2319        let data = unsafe {
2320            <&'static [bool] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2321        };
2322        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2323    }
2324}
2325
2326impl crate::altrep_traits::AltVec for &'static [bool] {}
2327
2328impl crate::altrep_traits::AltLogical for &'static [bool] {
2329    const HAS_ELT: bool = true;
2330
2331    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> i32 {
2332        let data = unsafe {
2333            <&'static [bool] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2334        };
2335        crate::altrep_data::AltLogicalData::elt(data, i.max(0) as usize).to_r_int()
2336    }
2337
2338    const HAS_NO_NA: bool = true;
2339
2340    fn no_na(x: crate::ffi::SEXP) -> i32 {
2341        let data = unsafe {
2342            <&'static [bool] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2343        };
2344        crate::altrep_data::AltLogicalData::no_na(data)
2345            .map(|b| if b { 1 } else { 0 })
2346            .unwrap_or(0)
2347    }
2348
2349    const HAS_SUM: bool = true;
2350
2351    // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
2352    fn sum(x: crate::ffi::SEXP, narm: bool) -> crate::ffi::SEXP {
2353        let data = unsafe {
2354            <&'static [bool] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2355        };
2356        crate::altrep_data::AltLogicalData::sum(data, narm)
2357            .map(|s| {
2358                if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
2359                    crate::ffi::SEXP::scalar_integer(s as i32)
2360                } else {
2361                    crate::ffi::SEXP::scalar_real(s as f64)
2362                }
2363            })
2364            .unwrap_or(crate::ffi::SEXP::null())
2365    }
2366}
2367
2368crate::impl_inferbase_logical!(&'static [bool]);
2369
2370// Raw static slices
2371impl crate::altrep_traits::Altrep for &'static [u8] {
2372    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2373        let data =
2374            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2375        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2376    }
2377}
2378
2379impl crate::altrep_traits::AltVec for &'static [u8] {
2380    const HAS_DATAPTR: bool = true;
2381
2382    fn dataptr(x: crate::ffi::SEXP, writable: bool) -> *mut std::ffi::c_void {
2383        assert!(
2384            !writable,
2385            "cannot get writable DATAPTR for static ALTREP data"
2386        );
2387        let data =
2388            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2389        data.as_ptr().cast::<std::ffi::c_void>().cast_mut()
2390    }
2391
2392    const HAS_DATAPTR_OR_NULL: bool = true;
2393
2394    fn dataptr_or_null(x: crate::ffi::SEXP) -> *const std::ffi::c_void {
2395        let data =
2396            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2397        data.as_ptr().cast::<std::ffi::c_void>()
2398    }
2399}
2400
2401impl crate::altrep_traits::AltRaw for &'static [u8] {
2402    const HAS_ELT: bool = true;
2403
2404    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::Rbyte {
2405        let data =
2406            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2407        crate::altrep_data::AltRawData::elt(data, i.max(0) as usize)
2408    }
2409
2410    const HAS_GET_REGION: bool = true;
2411
2412    fn get_region(
2413        x: crate::ffi::SEXP,
2414        start: crate::ffi::R_xlen_t,
2415        len: crate::ffi::R_xlen_t,
2416        buf: &mut [u8],
2417    ) -> crate::ffi::R_xlen_t {
2418        if start < 0 || len <= 0 {
2419            return 0;
2420        }
2421        let data =
2422            unsafe { <&'static [u8] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
2423        let len = len as usize;
2424        crate::altrep_data::AltRawData::get_region(data, start as usize, len, buf)
2425            as crate::ffi::R_xlen_t
2426    }
2427}
2428
2429crate::impl_inferbase_raw!(&'static [u8]);
2430
2431// String static slices (owned strings)
2432impl crate::altrep_traits::Altrep for &'static [String] {
2433    // String ALTREP elt calls Rf_mkCharLenCE (R API) — must use RUnwind.
2434    const GUARD: crate::altrep_traits::AltrepGuard = crate::altrep_traits::AltrepGuard::RUnwind;
2435
2436    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2437        let data = unsafe {
2438            <&'static [String] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2439        };
2440        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2441    }
2442}
2443
2444impl crate::altrep_traits::AltVec for &'static [String] {}
2445
2446impl crate::altrep_traits::AltString for &'static [String] {
2447    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::SEXP {
2448        let data = unsafe {
2449            <&'static [String] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2450        };
2451        match crate::altrep_data::AltStringData::elt(data, i.max(0) as usize) {
2452            Some(s) => unsafe { checked_mkchar(s) },
2453            None => crate::ffi::SEXP::na_string(),
2454        }
2455    }
2456
2457    const HAS_NO_NA: bool = true;
2458
2459    fn no_na(x: crate::ffi::SEXP) -> i32 {
2460        let data = unsafe {
2461            <&'static [String] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2462        };
2463        crate::altrep_data::AltStringData::no_na(data)
2464            .map(|b| if b { 1 } else { 0 })
2465            .unwrap_or(0)
2466    }
2467}
2468
2469crate::impl_inferbase_string!(&'static [String]);
2470
2471// String static slices (str references)
2472impl crate::altrep_traits::Altrep for &'static [&'static str] {
2473    // String ALTREP elt calls Rf_mkCharLenCE (R API) — must use RUnwind.
2474    const GUARD: crate::altrep_traits::AltrepGuard = crate::altrep_traits::AltrepGuard::RUnwind;
2475
2476    fn length(x: crate::ffi::SEXP) -> crate::ffi::R_xlen_t {
2477        let data = unsafe {
2478            <&'static [&'static str] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2479        };
2480        crate::altrep_data::AltrepLen::len(data) as crate::ffi::R_xlen_t
2481    }
2482}
2483
2484impl crate::altrep_traits::AltVec for &'static [&'static str] {}
2485
2486impl crate::altrep_traits::AltString for &'static [&'static str] {
2487    fn elt(x: crate::ffi::SEXP, i: crate::ffi::R_xlen_t) -> crate::ffi::SEXP {
2488        let data = unsafe {
2489            <&'static [&'static str] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2490        };
2491        match crate::altrep_data::AltStringData::elt(data, i.max(0) as usize) {
2492            Some(s) => unsafe { checked_mkchar(s) },
2493            None => crate::ffi::SEXP::na_string(),
2494        }
2495    }
2496
2497    const HAS_NO_NA: bool = true;
2498
2499    fn no_na(x: crate::ffi::SEXP) -> i32 {
2500        let data = unsafe {
2501            <&'static [&'static str] as crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
2502        };
2503        crate::altrep_data::AltStringData::no_na(data)
2504            .map(|b| if b { 1 } else { 0 })
2505            .unwrap_or(0)
2506    }
2507}
2508
2509crate::impl_inferbase_string!(&'static [&'static str]);
2510// endregion
2511
2512// region: RegisterAltrep implementations for builtin types
2513//
2514// These implementations provide ALTREP class registration for Vec<T>, Box<[T]>,
2515// and Range<T> types. They allow using these types with ALTREP via wrapper structs.
2516//
2517// Note: IntoR is NOT implemented here for Vec types because there are already
2518// existing IntoR implementations that copy data to R eagerly. To get ALTREP
2519// behavior, use wrapper structs:
2520//   #[miniextendr(class = "MyVec")]
2521//   pub struct MyVecClass(pub Vec<i32>);
2522//
2523// Each type uses a static OnceLock to cache the ALTREP class handle, which is
2524// registered on first use with the current package's name (from ALTREP_PKG_NAME).
2525
2526use crate::altrep::RegisterAltrep;
2527
2528/// Helper macro to implement RegisterAltrep for a builtin type.
2529macro_rules! impl_register_altrep_builtin {
2530    ($ty:ty, $class_name:expr) => {
2531        impl RegisterAltrep for $ty {
2532            fn get_or_init_class() -> crate::ffi::altrep::R_altrep_class_t {
2533                use std::sync::OnceLock;
2534                static CLASS: OnceLock<crate::ffi::altrep::R_altrep_class_t> = OnceLock::new();
2535                *CLASS.get_or_init(|| {
2536                    // Class name as null-terminated C string
2537                    const CLASS_NAME: &[u8] = concat!($class_name, "\0").as_bytes();
2538                    let cls = unsafe {
2539                        <$ty as crate::altrep_data::InferBase>::make_class(
2540                            CLASS_NAME.as_ptr().cast::<std::ffi::c_char>(),
2541                            crate::AltrepPkgName::as_ptr(),
2542                        )
2543                    };
2544                    unsafe {
2545                        <$ty as crate::altrep_data::InferBase>::install_methods(cls);
2546                    }
2547                    cls
2548                })
2549            }
2550        }
2551    };
2552}
2553
2554// Vec types - RegisterAltrep only (IntoR exists elsewhere, copies data)
2555impl_register_altrep_builtin!(Vec<i32>, "Vec_i32");
2556impl_register_altrep_builtin!(Vec<f64>, "Vec_f64");
2557impl_register_altrep_builtin!(Vec<bool>, "Vec_bool");
2558impl_register_altrep_builtin!(Vec<u8>, "Vec_u8");
2559impl_register_altrep_builtin!(Vec<String>, "Vec_String");
2560impl_register_altrep_builtin!(Vec<Option<String>>, "Vec_Option_String");
2561impl_register_altrep_builtin!(Vec<crate::ffi::Rcomplex>, "Vec_Rcomplex");
2562
2563// Range types - RegisterAltrep only
2564impl_register_altrep_builtin!(std::ops::Range<i32>, "Range_i32");
2565impl_register_altrep_builtin!(std::ops::Range<i64>, "Range_i64");
2566impl_register_altrep_builtin!(std::ops::Range<f64>, "Range_f64");
2567
2568// Box types - RegisterAltrep only
2569impl_register_altrep_builtin!(Box<[i32]>, "Box_i32");
2570impl_register_altrep_builtin!(Box<[f64]>, "Box_f64");
2571impl_register_altrep_builtin!(Box<[bool]>, "Box_bool");
2572impl_register_altrep_builtin!(Box<[u8]>, "Box_u8");
2573impl_register_altrep_builtin!(Box<[String]>, "Box_String");
2574impl_register_altrep_builtin!(Box<[crate::ffi::Rcomplex]>, "Box_Rcomplex");
2575
2576// Cow types - RegisterAltrep for zero-copy borrow from R
2577impl_register_altrep_builtin!(std::borrow::Cow<'static, [i32]>, "Cow_i32");
2578impl_register_altrep_builtin!(std::borrow::Cow<'static, [f64]>, "Cow_f64");
2579impl_register_altrep_builtin!(std::borrow::Cow<'static, [u8]>, "Cow_u8");
2580impl_register_altrep_builtin!(
2581    std::borrow::Cow<'static, [crate::ffi::Rcomplex]>,
2582    "Cow_Rcomplex"
2583);
2584
2585// Cow string vector types
2586impl_register_altrep_builtin!(Vec<std::borrow::Cow<'static, str>>, "Vec_Cow_str");
2587impl_register_altrep_builtin!(
2588    Vec<Option<std::borrow::Cow<'static, str>>>,
2589    "Vec_Option_Cow_str"
2590);
2591// endregion