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