Skip to main content

miniextendr_api/altrep_impl/
macros.rs

1//! Declarative macros generating ALTREP trait implementations.
2//!
3//! These `#[macro_export]` macros are the public surface used by the
4//! `#[derive(Altrep*)]` proc-macros and by hand-rolled `optionals/*` impls.
5//! They expand to `impl Altrep`, `impl AltVec`, `impl Alt<Family>`, and the
6//! per-family `InferBase` impl for a given type.
7//!
8//! Each per-family `impl_alt<family>_from_data!` macro takes a type plus an
9//! optional knob list of `dataptr` / `serialize` / `subset` (in canonical
10//! alphabetical order). The default arm (no knobs) is the materialising
11//! path — `__impl_altvec_<family>_dataptr!` provides a trivial
12//! `AltrepDataptr<T>` returning `None`, falling through to data2
13//! materialisation.
14//!
15//! Each family also has an `impl_alt<family>_from_data_generic!` sibling
16//! accepting a brace-delimited generic parameter list and where-clause
17//! (`{$gen} $ty {$where}[, $knob]*`) for generic types like
18//! `struct Foo<T> { .. }`. The plain macros forward to their `_generic!`
19//! sibling with empty `{}` brackets, so each family has exactly one emission
20//! body regardless of which form is called.
21//!
22//! Note the iterator/stream data adaptors in `altrep_data::iter` /
23//! `altrep_data::stream` deliberately do **not** invoke these macros: they
24//! implement only the data-level traits and are meant to be wrapped by a
25//! concrete `#[derive(Altrep*)]` + `#[altrep(manual)]` struct (#1146).
26
27// region: Macros for generating trait implementations
28
29/// Generate ALTREP trait implementations for a type that implements AltIntegerData.
30///
31/// This macro generates `impl Altrep`, `impl AltVec`, and `impl AltInteger` for the type,
32/// delegating to the high-level `AltIntegerData` trait methods.
33///
34/// **Requires**: The type must implement `TypedExternal` (use `#[derive(ExternalPtr)]`).
35///
36/// ## Variants
37///
38/// ```ignore
39/// // Basic (no dataptr, no serialization):
40/// impl_altinteger_from_data!(MyType);
41///
42/// // With dataptr (type must implement AltrepDataptr<i32>):
43/// impl_altinteger_from_data!(MyType, dataptr);
44///
45/// // With serialization (type must implement AltrepSerialize):
46/// impl_altinteger_from_data!(MyType, serialize);
47///
48/// // With subset optimization (type must implement AltrepExtractSubset):
49/// impl_altinteger_from_data!(MyType, subset);
50///
51/// // Combine multiple options:
52/// impl_altinteger_from_data!(MyType, dataptr, serialize);
53/// impl_altinteger_from_data!(MyType, subset, serialize);
54/// ```
55#[macro_export]
56macro_rules! impl_altinteger_from_data {
57    // Default (no knobs): materializing DATAPTR — allocates INTSXP in data2 on
58    // first DATAPTR call. Without this, R's default DATAPTR errors with
59    // "cannot access data pointer". See `__impl_alt_family!` for the knob matrix.
60    ($ty:ty $(, $knob:ident)*) => {
61        $crate::impl_altinteger_from_data_generic!({} $ty {} $(, $knob)*);
62    };
63}
64
65/// Generic form of [`impl_altinteger_from_data!`]: accepts an optional
66/// generic parameter list and where-clause (`{T, U} Foo<T, U> {T: Bound, ..}`)
67/// so it can target `struct Foo<T> { .. }` types, not just
68/// concrete/monomorphic ones. The non-generic macro above forwards here with
69/// empty `{}` brackets so there is exactly one emission body for both call
70/// shapes.
71#[macro_export]
72macro_rules! impl_altinteger_from_data_generic {
73    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
74        $crate::__impl_alt_family!(
75            {$($gen)*} $ty {$($whr)*},
76            __impl_altinteger_methods,
77            impl_inferbase_integer,
78            dataptr: dataptr(i32),
79            default: materializing(i32)
80            $(, $knob)*
81        );
82    };
83}
84
85/// Internal macro: impl Altrep with just length, optionally plus serialization.
86///
87/// ## Arms
88///
89/// ```ignore
90/// __impl_altrep_base!($ty);                            // length only, default RUnwind guard
91/// __impl_altrep_base!($ty, $guard);                    // length only, explicit guard
92/// __impl_altrep_base!($ty, with_serialize);            // length + serialize, default guard
93/// __impl_altrep_base!($ty, $guard, with_serialize);    // length + serialize, explicit guard
94/// ```
95///
96/// The `with_serialize` flag implements both:
97/// - `serialized_state(x)` (save-side)
98/// - `unserialize(class, state)` (load-side)
99///
100/// The `unserialize` implementation reconstructs the backing Rust value via
101/// [`AltrepSerialize::unserialize`] and then creates a fresh ALTREP instance via
102/// `R_new_altrep(class, data1, SEXP::nil())` where `data1` is an `ExternalPtr<$ty>`.
103///
104/// This matches the proc-macro-generated `IntoR::into_sexp` behavior (data is stored in `data1`,
105/// and `data2` is `R_NilValue`).
106#[macro_export]
107#[doc(hidden)]
108macro_rules! __impl_altrep_base {
109    ($ty:ty) => {
110        $crate::__impl_altrep_base!($ty, RUnwind);
111    };
112    ($ty:ty, with_serialize) => {
113        $crate::__impl_altrep_base!($ty, RUnwind, with_serialize);
114    };
115    // Generic form: `{$gen} $ty {$where}, $guard[, with_serialize]`. Brace
116    // (not bracket/paren) delimiters are deliberate: `{` can never start a
117    // `$ty:ty` fragment, so a bare-`$ty:ty` arm asked to match a brace-led
118    // invocation fails cleanly instead of committing to (and hard-erroring
119    // on) a bogus type parse — `[]`/`()` are themselves valid (empty
120    // slice-like / unit) type starts and would NOT fail cleanly here. The
121    // non-generic arms above forward to this one with empty `{}` brackets so
122    // there is exactly one emission body per (guard, serialize) combination.
123    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $guard:ident) => {
124        impl<$($gen)*> $crate::altrep_traits::Altrep for $ty where $($whr)* {
125            const GUARD: $crate::altrep_traits::AltrepGuard =
126                $crate::altrep_traits::AltrepGuard::$guard;
127
128            fn length(x: $crate::SEXP) -> $crate::R_xlen_t {
129                let data =
130                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
131                <$ty as $crate::altrep_data::AltrepLen>::len(data) as $crate::R_xlen_t
132            }
133        }
134    };
135    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $guard:ident, with_serialize) => {
136        impl<$($gen)*> $crate::altrep_traits::Altrep for $ty where $($whr)* {
137            const GUARD: $crate::altrep_traits::AltrepGuard =
138                $crate::altrep_traits::AltrepGuard::$guard;
139
140            fn length(x: $crate::SEXP) -> $crate::R_xlen_t {
141                let data =
142                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
143                <$ty as $crate::altrep_data::AltrepLen>::len(data) as $crate::R_xlen_t
144            }
145
146            const HAS_SERIALIZED_STATE: bool = true;
147
148            fn serialized_state(x: $crate::SEXP) -> $crate::SEXP {
149                let data =
150                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
151                <$ty as $crate::altrep_data::AltrepSerialize>::serialized_state(data)
152            }
153
154            const HAS_UNSERIALIZE: bool = true;
155
156            fn unserialize(class: $crate::SEXP, state: $crate::SEXP) -> $crate::SEXP {
157                let Some(data) = <$ty as $crate::altrep_data::AltrepSerialize>::unserialize(state)
158                else {
159                    panic!(
160                        "ALTREP unserialize failed for {}",
161                        core::any::type_name::<$ty>()
162                    );
163                };
164
165                // SAFETY: Unserialize is called by R on the main thread.
166                unsafe {
167                    use $crate::SEXP;
168                    use $crate::externalptr::ExternalPtr;
169                    use $crate::sys::altrep::R_altrep_class_t;
170                    use $crate::sys::{Rf_protect_unchecked, Rf_unprotect_unchecked};
171
172                    let ext_ptr = ExternalPtr::new_unchecked(data);
173                    let data1 = ext_ptr.as_sexp();
174                    // Protect across the allocation in new_altrep_unchecked.
175                    Rf_protect_unchecked(data1);
176                    let cls = R_altrep_class_t::from_sexp(class);
177                    let out = cls.new_altrep_unchecked(data1, SEXP::nil());
178                    Rf_unprotect_unchecked(1);
179                    out
180                }
181            }
182        }
183    };
184    // Bare-`$ty:ty` forwarders (concrete/monomorphic types) — listed AFTER
185    // the brace-generic arms above so a genuinely bare call (e.g. from the
186    // `#[derive(Altrep*)]` proc-macro, which always targets concrete types)
187    // still resolves here rather than being intercepted by the brace arms
188    // (which require a literal leading `{...}` group and so never match a
189    // bare invocation).
190    ($ty:ty, $guard:ident) => {
191        $crate::__impl_altrep_base!({} $ty {}, $guard);
192    };
193    ($ty:ty, $guard:ident, with_serialize) => {
194        $crate::__impl_altrep_base!({} $ty {}, $guard, with_serialize);
195    };
196}
197
198/// Internal macro: impl AltVec with dataptr support
199///
200/// When `writable = true`, obtains a mutable reference to the data via
201/// `altrep_data1_mut` so that writes through the returned pointer modify
202/// the Rust-owned data directly. When `writable = false`, uses the
203/// immutable `altrep_data1_as` + `dataptr_or_null` path, avoiding
204/// unnecessary mutable borrows (and, for `Cow`, avoiding a copy-on-write).
205#[macro_export]
206#[doc(hidden)]
207macro_rules! __impl_altvec_dataptr {
208    ($ty:ty, $elem:ty) => {
209        $crate::__impl_altvec_dataptr!({} $ty {}, $elem);
210    };
211    // Generic form: `[$gen] $ty [$where], $elem`. See `__impl_altrep_base!`
212    // for the empty-brackets delegation pattern.
213    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $elem:ty) => {
214        impl<$($gen)*> $crate::altrep_traits::AltVec for $ty where $($whr)* {
215            const HAS_DATAPTR: bool = true;
216
217            fn dataptr(x: $crate::SEXP, writable: bool) -> *mut core::ffi::c_void {
218                // Check data2 cache first (materialized by a prior call).
219                unsafe {
220                    let data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2_raw(&x);
221                    if !data2.is_null()
222                        && $crate::SexpExt::type_of(&data2)
223                            == <$elem as $crate::RNativeType>::SEXP_TYPE
224                    {
225                        return $crate::sys::DATAPTR_RO(data2).cast_mut();
226                    }
227                }
228
229                // Try the fast path: direct pointer from the underlying data.
230                let direct = if writable {
231                    let d = unsafe {
232                        <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
233                    };
234                    <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr(d, true)
235                        .map(|p| p.cast::<core::ffi::c_void>())
236                } else {
237                    // Read-only: try immutable access first to avoid &mut borrows
238                    // and unnecessary copy-on-write for Cow types.
239                    // Scoped block: the &T borrow must end before altrep_extract_mut
240                    // to avoid aliasing &T / &mut T (Stacked Borrows UB).
241                    let ro = {
242                        let d = unsafe {
243                            <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x)
244                        };
245                        <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr_or_null(d)
246                    };
247                    if let Some(p) = ro {
248                        return p.cast_mut().cast::<core::ffi::c_void>();
249                    }
250                    // dataptr_or_null returned None — try mutable path.
251                    let d = unsafe {
252                        <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_mut(x)
253                    };
254                    <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr(d, false)
255                        .map(|p| p.cast::<core::ffi::c_void>())
256                };
257
258                if let Some(p) = direct {
259                    return p;
260                }
261
262                // The underlying data can't provide a contiguous pointer (e.g., Arrow
263                // array with null bitmask). Materialize into data2 via Elt methods.
264                // Must never return null — R doesn't fall back when custom Dataptr is set.
265                unsafe { $crate::altrep_data::materialize_altrep_data2::<$elem>(x) }
266            }
267
268            const HAS_DATAPTR_OR_NULL: bool = true;
269
270            fn dataptr_or_null(x: $crate::SEXP) -> *const core::ffi::c_void {
271                // Check data2 cache first (may have been materialized by a prior dataptr call)
272                unsafe {
273                    let data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2_raw(&x);
274                    if !data2.is_null()
275                        && $crate::SexpExt::type_of(&data2)
276                            == <$elem as $crate::RNativeType>::SEXP_TYPE
277                    {
278                        return $crate::sys::DATAPTR_RO(data2);
279                    }
280                }
281                let d =
282                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
283                <$ty as $crate::altrep_data::AltrepDataptr<$elem>>::dataptr_or_null(d)
284                    .map(|p| p.cast::<core::ffi::c_void>())
285                    .unwrap_or(core::ptr::null())
286            }
287        }
288    };
289}
290
291/// Internal macro: impl AltVec with dataptr support for string ALTREP.
292///
293/// String vectors (STRSXP) store CHARSXP pointers, not contiguous data. This macro
294/// materializes remaining uncached elements into the data2 STRSXP cache (which may
295/// already have some elements from prior `Elt` calls). Returns the cached STRSXP's
296/// data pointer.
297#[macro_export]
298#[doc(hidden)]
299macro_rules! __impl_altvec_string_dataptr {
300    ($ty:ty) => {
301        $crate::__impl_altvec_string_dataptr!({} $ty {});
302    };
303    // Generic form: `[$gen] $ty [$where]`.
304    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
305        impl<$($gen)*> $crate::altrep_traits::AltVec for $ty where $($whr)* {
306            const HAS_DATAPTR: bool = true;
307
308            fn dataptr(x: $crate::SEXP, _writable: bool) -> *mut core::ffi::c_void {
309                unsafe {
310                    let n = <$ty as $crate::altrep_traits::Altrep>::length(x);
311
312                    // Get or allocate the data2 cache STRSXP
313                    let mut data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2_raw(&x);
314                    let fresh_alloc = data2.is_null()
315                        || $crate::SexpExt::type_of(&data2) != $crate::SEXPTYPE::STRSXP;
316                    if fresh_alloc {
317                        // Rf_allocVector(STRSXP, n) leaves elements UNINITIALIZED
318                        // (garbage SEXP pointers). Must fill with R_NaString sentinel
319                        // so cache lookups work. This is O(n) but unavoidable.
320                        //
321                        // Inside ALTREP dispatch — _unchecked variants skip the
322                        // with_r_thread debug-assert (MXL301 permits).
323                        data2 = $crate::sys::Rf_protect_unchecked(
324                            $crate::sys::Rf_allocVector_unchecked($crate::SEXPTYPE::STRSXP, n),
325                        );
326                        for j in 0..n {
327                            $crate::SexpExt::set_string_elt(&data2, j, $crate::SEXP::na_string());
328                        }
329                        $crate::altrep_ext::AltrepSexpExt::set_altrep_data2(&x, data2);
330                        $crate::sys::Rf_unprotect_unchecked(1);
331                    }
332
333                    // Fill uncached elements only — elements already cached by Elt
334                    // are non-NA CHARSXPs and are skipped. NA elements are re-probed
335                    // from Rust (O(1)) to handle mixed cached/uncached NA slots.
336                    for i in 0..n {
337                        let cached = $crate::SexpExt::string_elt(&data2, i);
338                        if cached != $crate::SEXP::na_string() {
339                            continue; // already cached by a prior Elt call
340                        }
341                        // Compute from Rust and store
342                        let elt = <$ty as $crate::altrep_traits::AltString>::elt(x, i);
343                        $crate::SexpExt::set_string_elt(&data2, i, elt);
344                    }
345
346                    $crate::sys::DATAPTR_RO(data2).cast_mut()
347                }
348            }
349
350            const HAS_DATAPTR_OR_NULL: bool = true;
351
352            fn dataptr_or_null(x: $crate::SEXP) -> *const core::ffi::c_void {
353                // Always return null. The data2 STRSXP may be partially cached
354                // (Elt filled some slots, others are R_NaString sentinels).
355                // Returning a pointer to a partial cache would expose sentinel
356                // R_NaString as actual NAs. Returning null tells R to use
357                // Elt-based access, which correctly handles the per-element cache.
358                // Dataptr (not dataptr_or_null) is the full-materialization path.
359                let _ = x;
360                core::ptr::null()
361            }
362        }
363    };
364}
365
366/// Internal macro: impl AltVec with a *materializing* dataptr for a given element type.
367///
368/// Thin wrapper, parameterised by element type: installs a trivial
369/// `AltrepDataptr<$elem>` (no direct pointer — `dataptr` returns `None`) and
370/// delegates to [`__impl_altvec_dataptr`], which materializes into `data2` via
371/// `RNativeType::elt`. The 5 per-family aliases below pin `$elem` so the derive
372/// and the public `impl_alt*_from_data!` macros can reference them by a stable
373/// per-family name.
374#[macro_export]
375#[doc(hidden)]
376macro_rules! __impl_altvec_materializing_dataptr {
377    ($ty:ty, $elem:ty) => {
378        $crate::__impl_altvec_materializing_dataptr!({} $ty {}, $elem);
379    };
380    // Generic form: `[$gen] $ty [$where], $elem`.
381    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $elem:ty) => {
382        impl<$($gen)*> $crate::altrep_data::AltrepDataptr<$elem> for $ty where $($whr)* {
383            fn dataptr(&mut self, _writable: bool) -> Option<*mut $elem> {
384                None
385            }
386        }
387        $crate::__impl_altvec_dataptr!({$($gen)*} $ty {$($whr)*}, $elem);
388    };
389}
390
391/// Internal macro: materializing dataptr for logical ALTREP.
392///
393/// R logicals are stored as `i32` but accessed through `RLogical` for type safety.
394#[macro_export]
395#[doc(hidden)]
396macro_rules! __impl_altvec_logical_dataptr {
397    ($ty:ty) => {
398        $crate::__impl_altvec_materializing_dataptr!($ty, $crate::RLogical);
399    };
400}
401
402/// Internal macro: materializing dataptr for integer ALTREP.
403#[macro_export]
404#[doc(hidden)]
405macro_rules! __impl_altvec_integer_dataptr {
406    ($ty:ty) => {
407        $crate::__impl_altvec_materializing_dataptr!($ty, i32);
408    };
409}
410
411/// Internal macro: materializing dataptr for real ALTREP.
412#[macro_export]
413#[doc(hidden)]
414macro_rules! __impl_altvec_real_dataptr {
415    ($ty:ty) => {
416        $crate::__impl_altvec_materializing_dataptr!($ty, f64);
417    };
418}
419
420/// Internal macro: materializing dataptr for raw ALTREP.
421#[macro_export]
422#[doc(hidden)]
423macro_rules! __impl_altvec_raw_dataptr {
424    ($ty:ty) => {
425        $crate::__impl_altvec_materializing_dataptr!($ty, u8);
426    };
427}
428
429/// Internal macro: materializing dataptr for complex ALTREP.
430#[macro_export]
431#[doc(hidden)]
432macro_rules! __impl_altvec_complex_dataptr {
433    ($ty:ty) => {
434        $crate::__impl_altvec_materializing_dataptr!($ty, $crate::Rcomplex);
435    };
436}
437
438/// Internal macro: impl AltVec with extract_subset support
439#[macro_export]
440#[doc(hidden)]
441macro_rules! __impl_altvec_extract_subset {
442    ($ty:ty) => {
443        $crate::__impl_altvec_extract_subset!({} $ty {});
444    };
445    // Generic form: `[$gen] $ty [$where]`.
446    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
447        impl<$($gen)*> $crate::altrep_traits::AltVec for $ty where $($whr)* {
448            const HAS_EXTRACT_SUBSET: bool = true;
449
450            fn extract_subset(
451                x: $crate::SEXP,
452                indx: $crate::SEXP,
453                _call: $crate::SEXP,
454            ) -> $crate::SEXP {
455                // Validate that indx is an integer vector before calling INTEGER().
456                // Return C NULL (not R_NilValue) to signal R to use default
457                // subsetting if not — R checks `!= NULL` here.
458                if $crate::SexpExt::type_of(&indx) != $crate::SEXPTYPE::INTSXP {
459                    return $crate::SEXP::null();
460                }
461
462                // Convert indx SEXP to slice using SexpExt (avoids raw-ptr-deref lint)
463                let indices = unsafe { $crate::SexpExt::as_slice::<i32>(&indx) };
464
465                let data =
466                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
467                <$ty as $crate::altrep_data::AltrepExtractSubset>::extract_subset(data, indices)
468                    .unwrap_or($crate::SEXP::nil())
469            }
470        }
471    };
472}
473// endregion
474
475// region: Shared building-block macros for ALTREP trait implementations
476//
477// These macros expand to associated items inside `impl` blocks. They are
478// invoked by the per-family `__impl_alt*_methods!` macros to eliminate
479// code duplication across the 7 ALTREP type families.
480
481/// Shared `elt` implementation for ALTREP families with direct element access.
482///
483/// Generates `const HAS_ELT` and `fn elt(...)` inside an impl block.
484/// Used by integer, real, raw, and complex families.
485#[macro_export]
486#[doc(hidden)]
487macro_rules! __impl_alt_elt {
488    ($ty:ty, $trait:path, $elem:ty, $na:expr) => {
489        const HAS_ELT: bool = true;
490
491        fn elt(x: $crate::SEXP, i: $crate::R_xlen_t) -> $elem {
492            let data =
493                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
494            <$ty as $trait>::elt(data, i.max(0) as usize)
495        }
496    };
497}
498
499/// Shared `get_region` implementation for ALTREP families.
500///
501/// Generates `const HAS_GET_REGION` and `fn get_region(...)` inside an impl block.
502/// Used by integer, real, logical, raw, and complex families.
503#[macro_export]
504#[doc(hidden)]
505macro_rules! __impl_alt_get_region {
506    ($ty:ty, $trait:path, $buf_ty:ty) => {
507        const HAS_GET_REGION: bool = true;
508
509        fn get_region(
510            x: $crate::SEXP,
511            start: $crate::R_xlen_t,
512            len: $crate::R_xlen_t,
513            buf: &mut [$buf_ty],
514        ) -> $crate::R_xlen_t {
515            if start < 0 || len <= 0 {
516                return 0;
517            }
518            let data =
519                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
520            let len = len as usize;
521            <$ty as $trait>::get_region(data, start as usize, len, buf) as $crate::R_xlen_t
522        }
523    };
524}
525
526/// Shared `is_sorted` implementation for ALTREP families.
527///
528/// Generates `const HAS_IS_SORTED` and `fn is_sorted(...)` inside an impl block.
529/// Used by integer, real, logical, and string families.
530#[macro_export]
531#[doc(hidden)]
532macro_rules! __impl_alt_is_sorted {
533    ($ty:ty, $trait:path) => {
534        const HAS_IS_SORTED: bool = true;
535
536        fn is_sorted(x: $crate::SEXP) -> i32 {
537            let data =
538                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
539            <$ty as $trait>::is_sorted(data)
540                .map(|s| s.to_r_int())
541                .unwrap_or(i32::MIN)
542        }
543    };
544}
545
546/// Shared `no_na` implementation for ALTREP families.
547///
548/// Generates `const HAS_NO_NA` and `fn no_na(...)` inside an impl block.
549/// Used by integer, real, logical, and string families.
550#[macro_export]
551#[doc(hidden)]
552macro_rules! __impl_alt_no_na {
553    ($ty:ty, $trait:path) => {
554        const HAS_NO_NA: bool = true;
555
556        fn no_na(x: $crate::SEXP) -> i32 {
557            let data =
558                unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
559            <$ty as $trait>::no_na(data)
560                .map(|b| if b { 1 } else { 0 })
561                .unwrap_or(0)
562        }
563    };
564}
565// endregion
566
567// region: Canonical emission macros: __impl_altvec_flavor! / __impl_alt_from_data! / __impl_alt_family!
568//
569// Three layers, each with a single responsibility:
570//
571// 1. `__impl_altvec_flavor!` — maps an AltVec *flavor* token to the macro
572//    that emits the `impl AltVec` (typed direct pointer, materializing,
573//    STRSXP materialization, or extract_subset).
574// 2. `__impl_alt_from_data!` — the canonical emitter: Altrep base
575//    (with/without serialize) + AltVec flavor + family methods + InferBase.
576//    Exactly two arms — every knob combination reduces to these.
577// 3. `__impl_alt_family!` — the knob matrix: maps the public macros'
578//    user-facing knob spellings (`dataptr` / `serialize` / `subset`, in
579//    canonical order) to a flavor + optional serialize. Parameterised by the
580//    family's `dataptr:` and `default:` flavors so one matrix serves all
581//    six knob-bearing families.
582
583/// Internal macro: emit the `impl AltVec` for a given flavor.
584///
585/// Flavors:
586/// - `dataptr($elem)` — typed direct pointer via `AltrepDataptr<$elem>`,
587///   falling back to data2 materialization.
588/// - `materializing($elem)` — trivial `AltrepDataptr<$elem>` returning `None`
589///   plus the same `__impl_altvec_dataptr!` (pure data2 materialization).
590/// - `string_dataptr` — whole-vector STRSXP materialization (string family).
591/// - `subset` — `Extract_subset` support via `AltrepExtractSubset`.
592#[macro_export]
593#[doc(hidden)]
594macro_rules! __impl_altvec_flavor {
595    ($ty:ty, dataptr($elem:ty)) => {
596        $crate::__impl_altvec_dataptr!($ty, $elem);
597    };
598    ($ty:ty, materializing($elem:ty)) => {
599        $crate::__impl_altvec_materializing_dataptr!($ty, $elem);
600    };
601    ($ty:ty, string_dataptr) => {
602        $crate::__impl_altvec_string_dataptr!($ty);
603    };
604    ($ty:ty, subset) => {
605        $crate::__impl_altvec_extract_subset!($ty);
606    };
607    // Generic form: `[$gen] $ty [$where], $flavor`.
608    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, dataptr($elem:ty)) => {
609        $crate::__impl_altvec_dataptr!({$($gen)*} $ty {$($whr)*}, $elem);
610    };
611    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, materializing($elem:ty)) => {
612        $crate::__impl_altvec_materializing_dataptr!({$($gen)*} $ty {$($whr)*}, $elem);
613    };
614    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, string_dataptr) => {
615        $crate::__impl_altvec_string_dataptr!({$($gen)*} $ty {$($whr)*});
616    };
617    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, subset) => {
618        $crate::__impl_altvec_extract_subset!({$($gen)*} $ty {$($whr)*});
619    };
620}
621
622/// Internal macro: canonical ALTREP emission.
623///
624/// Generates the standard ALTREP trait implementations (Altrep base, AltVec
625/// flavor, family-specific methods, InferBase) for a given type. The two arms
626/// differ only in serialization support on the Altrep base impl.
627#[macro_export]
628#[doc(hidden)]
629macro_rules! __impl_alt_from_data {
630    ($ty:ty, $methods:ident, $inferbase:ident, $flavor:ident $(($elem:ty))?) => {
631        $crate::__impl_altrep_base!($ty);
632        $crate::__impl_altvec_flavor!($ty, $flavor $(($elem))?);
633        $crate::$methods!($ty);
634        $crate::$inferbase!($ty);
635    };
636    ($ty:ty, $methods:ident, $inferbase:ident, $flavor:ident $(($elem:ty))?, serialize) => {
637        $crate::__impl_altrep_base!($ty, with_serialize);
638        $crate::__impl_altvec_flavor!($ty, $flavor $(($elem))?);
639        $crate::$methods!($ty);
640        $crate::$inferbase!($ty);
641    };
642    // Generic form: `[$gen] $ty [$where], $methods, $inferbase, $flavor`.
643    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident, $flavor:ident $(($elem:ty))?) => {
644        $crate::__impl_altrep_base!({$($gen)*} $ty {$($whr)*}, RUnwind);
645        $crate::__impl_altvec_flavor!({$($gen)*} $ty {$($whr)*}, $flavor $(($elem))?);
646        $crate::$methods!({$($gen)*} $ty {$($whr)*});
647        $crate::$inferbase!({$($gen)*} $ty {$($whr)*});
648    };
649    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident, $flavor:ident $(($elem:ty))?, serialize) => {
650        $crate::__impl_altrep_base!({$($gen)*} $ty {$($whr)*}, RUnwind, with_serialize);
651        $crate::__impl_altvec_flavor!({$($gen)*} $ty {$($whr)*}, $flavor $(($elem))?);
652        $crate::$methods!({$($gen)*} $ty {$($whr)*});
653        $crate::$inferbase!({$($gen)*} $ty {$($whr)*});
654    };
655}
656
657/// Internal macro: the per-family knob matrix.
658///
659/// Maps the user-facing knob list of the public `impl_alt*_from_data!` macros
660/// (`dataptr` / `serialize` / `subset`, canonical order) to a canonical
661/// [`__impl_alt_from_data!`] invocation. The family supplies:
662/// - `dataptr:` — the AltVec flavor for the `dataptr` knob,
663/// - `default:` — the AltVec flavor when no `dataptr`/`subset` knob is given
664///   (the materializing/data2 path).
665///
666/// Valid knob combinations (anything else is a compile error):
667///
668/// ```ignore
669/// ()                    // default flavor
670/// (dataptr)             // direct-pointer flavor
671/// (serialize)           // default flavor + serialize
672/// (subset)              // extract_subset flavor
673/// (dataptr, serialize)
674/// (subset, serialize)
675/// ```
676#[macro_export]
677#[doc(hidden)]
678macro_rules! __impl_alt_family {
679    ($ty:ty, $methods:ident, $inferbase:ident,
680     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?) => {
681        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, $dff $(($dfe))?);
682    };
683    ($ty:ty, $methods:ident, $inferbase:ident,
684     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, dataptr) => {
685        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, $dpf $(($dpe))?);
686    };
687    ($ty:ty, $methods:ident, $inferbase:ident,
688     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, serialize) => {
689        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, $dff $(($dfe))?, serialize);
690    };
691    ($ty:ty, $methods:ident, $inferbase:ident,
692     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, subset) => {
693        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, subset);
694    };
695    ($ty:ty, $methods:ident, $inferbase:ident,
696     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, dataptr, serialize) => {
697        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, $dpf $(($dpe))?, serialize);
698    };
699    ($ty:ty, $methods:ident, $inferbase:ident,
700     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, subset, serialize) => {
701        $crate::__impl_alt_from_data!($ty, $methods, $inferbase, subset, serialize);
702    };
703    // Generic form: `[$gen] $ty [$where], $methods, $inferbase, dataptr: .., default: ..[, knobs]`.
704    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
705     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?) => {
706        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, $dff $(($dfe))?);
707    };
708    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
709     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, dataptr) => {
710        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, $dpf $(($dpe))?);
711    };
712    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
713     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, serialize) => {
714        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, $dff $(($dfe))?, serialize);
715    };
716    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
717     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, subset) => {
718        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, subset);
719    };
720    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
721     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, dataptr, serialize) => {
722        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, $dpf $(($dpe))?, serialize);
723    };
724    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $methods:ident, $inferbase:ident,
725     dataptr: $dpf:ident $(($dpe:ty))?, default: $dff:ident $(($dfe:ty))?, subset, serialize) => {
726        $crate::__impl_alt_from_data!({$($gen)*} $ty {$($whr)*}, $methods, $inferbase, subset, serialize);
727    };
728}
729// endregion
730
731// region: Per-family method macros (using shared building blocks)
732
733/// Internal macro for AltInteger method implementations.
734#[macro_export]
735#[doc(hidden)]
736macro_rules! __impl_altinteger_methods {
737    ($ty:ty) => {
738        $crate::__impl_altinteger_methods!({} $ty {});
739    };
740    // Generic form: `[$gen] $ty [$where]`.
741    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
742        impl<$($gen)*> $crate::altrep_traits::AltInteger for $ty where $($whr)* {
743            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltIntegerData, i32, i32::MIN);
744            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltIntegerData, i32);
745            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltIntegerData);
746            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltIntegerData);
747
748            const HAS_SUM: bool = true;
749
750            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
751            fn sum(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
752                let data =
753                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
754                match <$ty as $crate::altrep_data::AltIntegerData>::sum(data, narm) {
755                    Some(s) => {
756                        if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
757                            $crate::SEXP::scalar_integer(s as i32)
758                        } else {
759                            $crate::SEXP::scalar_real(s as f64)
760                        }
761                    }
762                    None => $crate::SEXP::null(),
763                }
764            }
765
766            const HAS_MIN: bool = true;
767
768            fn min(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
769                let data =
770                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
771                <$ty as $crate::altrep_data::AltIntegerData>::min(data, narm)
772                    .map(|m| $crate::SEXP::scalar_integer(m))
773                    .unwrap_or($crate::SEXP::null())
774            }
775
776            const HAS_MAX: bool = true;
777
778            fn max(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
779                let data =
780                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
781                <$ty as $crate::altrep_data::AltIntegerData>::max(data, narm)
782                    .map(|m| $crate::SEXP::scalar_integer(m))
783                    .unwrap_or($crate::SEXP::null())
784            }
785        }
786    };
787}
788
789/// Generate ALTREP trait implementations for a type that implements AltRealData.
790///
791/// ## Variants
792///
793/// ```ignore
794/// // Basic (no dataptr, no serialization):
795/// impl_altreal_from_data!(MyType);
796///
797/// // With dataptr (type must implement AltrepDataptr<f64>):
798/// impl_altreal_from_data!(MyType, dataptr);
799///
800/// // With serialization (type must implement AltrepSerialize):
801/// impl_altreal_from_data!(MyType, serialize);
802///
803/// // With subset optimization (type must implement AltrepExtractSubset):
804/// impl_altreal_from_data!(MyType, subset);
805///
806/// // Combine multiple options:
807/// impl_altreal_from_data!(MyType, dataptr, serialize);
808/// impl_altreal_from_data!(MyType, subset, serialize);
809/// ```
810#[macro_export]
811macro_rules! impl_altreal_from_data {
812    ($ty:ty $(, $knob:ident)*) => {
813        $crate::impl_altreal_from_data_generic!({} $ty {} $(, $knob)*);
814    };
815}
816
817/// Generic form of [`impl_altreal_from_data!`] — see
818/// [`impl_altinteger_from_data_generic!`] for the calling convention.
819#[macro_export]
820macro_rules! impl_altreal_from_data_generic {
821    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
822        $crate::__impl_alt_family!(
823            {$($gen)*} $ty {$($whr)*},
824            __impl_altreal_methods,
825            impl_inferbase_real,
826            dataptr: dataptr(f64),
827            default: materializing(f64)
828            $(, $knob)*
829        );
830    };
831}
832
833/// Internal macro for AltReal method implementations.
834#[macro_export]
835#[doc(hidden)]
836macro_rules! __impl_altreal_methods {
837    ($ty:ty) => {
838        $crate::__impl_altreal_methods!({} $ty {});
839    };
840    // Generic form: `[$gen] $ty [$where]`.
841    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
842        impl<$($gen)*> $crate::altrep_traits::AltReal for $ty where $($whr)* {
843            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltRealData, f64, f64::NAN);
844            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltRealData, f64);
845            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltRealData);
846            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltRealData);
847
848            const HAS_SUM: bool = true;
849
850            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
851            fn sum(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
852                let data =
853                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
854                <$ty as $crate::altrep_data::AltRealData>::sum(data, narm)
855                    .map(|s| $crate::SEXP::scalar_real(s))
856                    .unwrap_or($crate::SEXP::null())
857            }
858
859            const HAS_MIN: bool = true;
860
861            fn min(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
862                let data =
863                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
864                <$ty as $crate::altrep_data::AltRealData>::min(data, narm)
865                    .map(|m| $crate::SEXP::scalar_real(m))
866                    .unwrap_or($crate::SEXP::null())
867            }
868
869            const HAS_MAX: bool = true;
870
871            fn max(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
872                let data =
873                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
874                <$ty as $crate::altrep_data::AltRealData>::max(data, narm)
875                    .map(|m| $crate::SEXP::scalar_real(m))
876                    .unwrap_or($crate::SEXP::null())
877            }
878        }
879    };
880}
881
882/// Generate ALTREP trait implementations for a type that implements AltLogicalData.
883///
884/// ## Variants
885///
886/// ```ignore
887/// // Basic (no dataptr, no serialization):
888/// impl_altlogical_from_data!(MyType);
889///
890/// // With dataptr (type must implement AltrepDataptr<i32>):
891/// impl_altlogical_from_data!(MyType, dataptr);
892///
893/// // With serialization (type must implement AltrepSerialize):
894/// impl_altlogical_from_data!(MyType, serialize);
895///
896/// // With subset optimization (type must implement AltrepExtractSubset):
897/// impl_altlogical_from_data!(MyType, subset);
898///
899/// // Combine multiple options:
900/// impl_altlogical_from_data!(MyType, dataptr, serialize);
901/// impl_altlogical_from_data!(MyType, subset, serialize);
902/// ```
903#[macro_export]
904macro_rules! impl_altlogical_from_data {
905    // Note the asymmetry: the `dataptr` knob is typed `i32` (R's LGLSXP storage),
906    // while the materializing default goes through `RLogical` for type safety.
907    ($ty:ty $(, $knob:ident)*) => {
908        $crate::impl_altlogical_from_data_generic!({} $ty {} $(, $knob)*);
909    };
910}
911
912/// Generic form of [`impl_altlogical_from_data!`] — see
913/// [`impl_altinteger_from_data_generic!`] for the calling convention.
914#[macro_export]
915macro_rules! impl_altlogical_from_data_generic {
916    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
917        $crate::__impl_alt_family!(
918            {$($gen)*} $ty {$($whr)*},
919            __impl_altlogical_methods,
920            impl_inferbase_logical,
921            dataptr: dataptr(i32),
922            default: materializing($crate::RLogical)
923            $(, $knob)*
924        );
925    };
926}
927
928/// Internal macro: impl AltLogical methods from AltLogicalData
929#[macro_export]
930#[doc(hidden)]
931macro_rules! __impl_altlogical_methods {
932    ($ty:ty) => {
933        $crate::__impl_altlogical_methods!({} $ty {});
934    };
935    // Generic form: `[$gen] $ty [$where]`.
936    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
937        impl<$($gen)*> $crate::altrep_traits::AltLogical for $ty where $($whr)* {
938            // Logical elt is special: returns Logical → .to_r_int()
939            const HAS_ELT: bool = true;
940
941            fn elt(x: $crate::SEXP, i: $crate::R_xlen_t) -> i32 {
942                let data =
943                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
944                <$ty as $crate::altrep_data::AltLogicalData>::elt(data, i.max(0) as usize)
945                    .to_r_int()
946            }
947
948            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltLogicalData, i32);
949            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltLogicalData);
950            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltLogicalData);
951
952            const HAS_SUM: bool = true;
953
954            // ALTREP protocol: return C NULL (not R_NilValue) to signal "can't compute"
955            fn sum(x: $crate::SEXP, narm: bool) -> $crate::SEXP {
956                let data =
957                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
958                match <$ty as $crate::altrep_data::AltLogicalData>::sum(data, narm) {
959                    Some(s) => {
960                        if s >= i32::MIN as i64 && s <= i32::MAX as i64 {
961                            $crate::SEXP::scalar_integer(s as i32)
962                        } else {
963                            $crate::SEXP::scalar_real(s as f64)
964                        }
965                    }
966                    None => $crate::SEXP::null(),
967                }
968            }
969        }
970    };
971}
972
973/// Generate ALTREP trait implementations for a type that implements AltRawData.
974///
975/// ## Variants
976///
977/// ```ignore
978/// // Basic (no dataptr, no serialization):
979/// impl_altraw_from_data!(MyType);
980///
981/// // With dataptr (type must implement AltrepDataptr<u8>):
982/// impl_altraw_from_data!(MyType, dataptr);
983///
984/// // With serialization (type must implement AltrepSerialize):
985/// impl_altraw_from_data!(MyType, serialize);
986///
987/// // With subset optimization (type must implement AltrepExtractSubset):
988/// impl_altraw_from_data!(MyType, subset);
989///
990/// // Combine multiple options:
991/// impl_altraw_from_data!(MyType, dataptr, serialize);
992/// impl_altraw_from_data!(MyType, subset, serialize);
993/// ```
994#[macro_export]
995macro_rules! impl_altraw_from_data {
996    ($ty:ty $(, $knob:ident)*) => {
997        $crate::impl_altraw_from_data_generic!({} $ty {} $(, $knob)*);
998    };
999}
1000
1001/// Generic form of [`impl_altraw_from_data!`] — see
1002/// [`impl_altinteger_from_data_generic!`] for the calling convention.
1003#[macro_export]
1004macro_rules! impl_altraw_from_data_generic {
1005    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
1006        $crate::__impl_alt_family!(
1007            {$($gen)*} $ty {$($whr)*},
1008            __impl_altraw_methods,
1009            impl_inferbase_raw,
1010            dataptr: dataptr(u8),
1011            default: materializing(u8)
1012            $(, $knob)*
1013        );
1014    };
1015}
1016
1017/// Internal macro for AltRaw method implementations.
1018#[macro_export]
1019#[doc(hidden)]
1020macro_rules! __impl_altraw_methods {
1021    ($ty:ty) => {
1022        $crate::__impl_altraw_methods!({} $ty {});
1023    };
1024    // Generic form: `[$gen] $ty [$where]`.
1025    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
1026        impl<$($gen)*> $crate::altrep_traits::AltRaw for $ty where $($whr)* {
1027            $crate::__impl_alt_elt!($ty, $crate::altrep_data::AltRawData, u8, 0);
1028            $crate::__impl_alt_get_region!($ty, $crate::altrep_data::AltRawData, u8);
1029        }
1030    };
1031}
1032
1033/// Generate ALTREP trait implementations for a type that implements AltStringData.
1034///
1035/// ## Variants
1036///
1037/// ```ignore
1038/// // Basic (no serialization):
1039/// impl_altstring_from_data!(MyType);
1040///
1041/// // With dataptr (materialized STRSXP):
1042/// impl_altstring_from_data!(MyType, dataptr);
1043///
1044/// // With serialization (type must implement AltrepSerialize):
1045/// impl_altstring_from_data!(MyType, serialize);
1046///
1047/// // With subset optimization (type must implement AltrepExtractSubset):
1048/// impl_altstring_from_data!(MyType, subset);
1049///
1050/// // Combine multiple options:
1051/// impl_altstring_from_data!(MyType, dataptr, serialize);
1052/// impl_altstring_from_data!(MyType, subset, serialize);
1053/// ```
1054#[macro_export]
1055macro_rules! impl_altstring_from_data {
1056    // String vectors have no contiguous typed pointer; the default and `dataptr`
1057    // knobs both route through `string_dataptr` (whole-vector STRSXP materialization).
1058    ($ty:ty $(, $knob:ident)*) => {
1059        $crate::impl_altstring_from_data_generic!({} $ty {} $(, $knob)*);
1060    };
1061}
1062
1063/// Generic form of [`impl_altstring_from_data!`] — see
1064/// [`impl_altinteger_from_data_generic!`] for the calling convention.
1065#[macro_export]
1066macro_rules! impl_altstring_from_data_generic {
1067    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
1068        $crate::__impl_alt_family!(
1069            {$($gen)*} $ty {$($whr)*},
1070            __impl_altstring_methods,
1071            impl_inferbase_string,
1072            dataptr: string_dataptr,
1073            default: string_dataptr
1074            $(, $knob)*
1075        );
1076    };
1077}
1078
1079/// Internal macro for AltString method implementations.
1080#[macro_export]
1081#[doc(hidden)]
1082macro_rules! __impl_altstring_methods {
1083    ($ty:ty) => {
1084        $crate::__impl_altstring_methods!({} $ty {});
1085    };
1086    // Generic form: `[$gen] $ty [$where]`.
1087    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
1088        impl<$($gen)*> $crate::altrep_traits::AltString for $ty where $($whr)* {
1089            // String elt with lazy per-element caching in data2 STRSXP.
1090            //
1091            // On first access, allocates a STRSXP in data2 (initialized to R_NaString).
1092            // Each element is computed from Rust on first access and cached. Subsequent
1093            // accesses return the cached CHARSXP directly.
1094            //
1095            // For NA elements (Rust elt returns None), data2[i] stays R_NaString — we
1096            // re-probe Rust each time (O(1) index, returns None immediately). This is
1097            // simpler than a separate materialization bitmap and the cost is negligible.
1098            fn elt(x: $crate::SEXP, i: $crate::R_xlen_t) -> $crate::SEXP {
1099                unsafe {
1100                    let idx = i.max(0) as usize;
1101
1102                    // Get or allocate the data2 cache STRSXP
1103                    let mut data2 = $crate::altrep_ext::AltrepSexpExt::altrep_data2_raw(&x);
1104                    if data2.is_null()
1105                        || $crate::SexpExt::type_of(&data2) != $crate::SEXPTYPE::STRSXP
1106                    {
1107                        let n = <$ty as $crate::altrep_traits::Altrep>::length(x);
1108                        // Rf_allocVector(STRSXP, n) leaves elements UNINITIALIZED
1109                        // (garbage SEXP pointers). Must fill with R_NaString sentinel.
1110                        //
1111                        // Inside ALTREP dispatch — _unchecked variants skip the
1112                        // with_r_thread debug-assert (MXL301 permits).
1113                        data2 = $crate::sys::Rf_protect_unchecked(
1114                            $crate::sys::Rf_allocVector_unchecked($crate::SEXPTYPE::STRSXP, n),
1115                        );
1116                        for j in 0..n {
1117                            $crate::SexpExt::set_string_elt(&data2, j, $crate::SEXP::na_string());
1118                        }
1119                        $crate::altrep_ext::AltrepSexpExt::set_altrep_data2(&x, data2);
1120                        $crate::sys::Rf_unprotect_unchecked(1);
1121                    }
1122
1123                    // Check cache: non-NA means already materialized
1124                    let cached = $crate::SexpExt::string_elt(&data2, i);
1125                    if cached != $crate::SEXP::na_string() {
1126                        return cached;
1127                    }
1128
1129                    // Cache miss (or genuine NA) — probe Rust source
1130                    let data = <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x);
1131                    match <$ty as $crate::altrep_data::AltStringData>::elt(data, idx) {
1132                        Some(s) => {
1133                            let charsxp = $crate::altrep_impl::checked_mkchar(s);
1134                            $crate::SexpExt::set_string_elt(&data2, i, charsxp);
1135                            charsxp
1136                        }
1137                        None => $crate::SEXP::na_string(),
1138                    }
1139                }
1140            }
1141
1142            $crate::__impl_alt_is_sorted!($ty, $crate::altrep_data::AltStringData);
1143            $crate::__impl_alt_no_na!($ty, $crate::altrep_data::AltStringData);
1144        }
1145    };
1146}
1147
1148/// Generate ALTREP trait implementations for a type that implements AltListData.
1149#[macro_export]
1150macro_rules! impl_altlist_from_data {
1151    ($ty:ty) => {
1152        $crate::impl_altlist_from_data!($ty, RUnwind);
1153    };
1154    ($ty:ty, $guard:ident) => {
1155        $crate::impl_altlist_from_data_generic!({} $ty {}, $guard);
1156    };
1157}
1158
1159/// Generic form of [`impl_altlist_from_data!`]: accepts an optional generic
1160/// parameter list and where-clause so it can target `struct Foo<T> { .. }`
1161/// types, not just concrete ones. The non-generic macro above forwards
1162/// here with empty `{}` brackets so there is exactly one emission body.
1163#[macro_export]
1164macro_rules! impl_altlist_from_data_generic {
1165    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
1166        $crate::impl_altlist_from_data_generic!({$($gen)*} $ty {$($whr)*}, RUnwind);
1167    };
1168    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}, $guard:ident) => {
1169        impl<$($gen)*> $crate::altrep_traits::Altrep for $ty where $($whr)* {
1170            const GUARD: $crate::altrep_traits::AltrepGuard =
1171                $crate::altrep_traits::AltrepGuard::$guard;
1172
1173            fn length(x: $crate::SEXP) -> $crate::R_xlen_t {
1174                let data =
1175                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1176                <$ty as $crate::altrep_data::AltrepLen>::len(data) as $crate::R_xlen_t
1177            }
1178        }
1179
1180        impl<$($gen)*> $crate::altrep_traits::AltVec for $ty where $($whr)* {}
1181
1182        impl<$($gen)*> $crate::altrep_traits::AltList for $ty where $($whr)* {
1183            fn elt(x: $crate::SEXP, i: $crate::R_xlen_t) -> $crate::SEXP {
1184                let data =
1185                    unsafe { <$ty as $crate::altrep_data::AltrepExtract>::altrep_extract_ref(x) };
1186                <$ty as $crate::altrep_data::AltListData>::elt(data, i.max(0) as usize)
1187            }
1188        }
1189
1190        $crate::impl_inferbase_list!({$($gen)*} $ty {$($whr)*});
1191    };
1192}
1193
1194/// Internal macro: impl AltComplex methods (elt, get_region)
1195#[macro_export]
1196#[doc(hidden)]
1197macro_rules! __impl_altcomplex_methods {
1198    ($ty:ty) => {
1199        $crate::__impl_altcomplex_methods!({} $ty {});
1200    };
1201    // Generic form: `[$gen] $ty [$where]`.
1202    ({$($gen:tt)*} $ty:ty {$($whr:tt)*}) => {
1203        impl<$($gen)*> $crate::altrep_traits::AltComplex for $ty where $($whr)* {
1204            $crate::__impl_alt_elt!(
1205                $ty,
1206                $crate::altrep_data::AltComplexData,
1207                $crate::Rcomplex,
1208                $crate::Rcomplex {
1209                    r: f64::NAN,
1210                    i: f64::NAN
1211                }
1212            );
1213            $crate::__impl_alt_get_region!(
1214                $ty,
1215                $crate::altrep_data::AltComplexData,
1216                $crate::Rcomplex
1217            );
1218        }
1219    };
1220}
1221
1222/// Generate ALTREP trait implementations for a type that implements AltComplexData.
1223///
1224/// Optional features can be enabled by passing additional arguments:
1225/// - `dataptr`: Enable `Dataptr` and `Dataptr_or_null` methods (requires `AltrepDataptr<Rcomplex>`)
1226/// - `serialize`: Enable serialization support (requires `AltrepSerialize`)
1227/// - `subset`: Enable optimized subsetting (requires `AltrepExtractSubset`)
1228#[macro_export]
1229macro_rules! impl_altcomplex_from_data {
1230    ($ty:ty $(, $knob:ident)*) => {
1231        $crate::impl_altcomplex_from_data_generic!({} $ty {} $(, $knob)*);
1232    };
1233}
1234
1235/// Generic form of [`impl_altcomplex_from_data!`] — see
1236/// [`impl_altinteger_from_data_generic!`] for the calling convention.
1237#[macro_export]
1238macro_rules! impl_altcomplex_from_data_generic {
1239    ({$($gen:tt)*} $ty:ty {$($whr:tt)*} $(, $knob:ident)*) => {
1240        $crate::__impl_alt_family!(
1241            {$($gen)*} $ty {$($whr)*},
1242            __impl_altcomplex_methods,
1243            impl_inferbase_complex,
1244            dataptr: dataptr($crate::Rcomplex),
1245            default: materializing($crate::Rcomplex)
1246            $(, $knob)*
1247        );
1248    };
1249}
1250// endregion