miniextendr_api/altrep_traits.rs
1//! Safe, idiomatic ALTREP trait hierarchy mirroring R's method tables.
2//!
3//! ## Design: Required vs Optional Methods
4//!
5//! Each ALTREP type family has:
6//! - **Required methods** (no defaults) - compiler enforces implementation
7//! - **Optional methods** with `HAS_*` const gating - defaults to `false` (not installed)
8//!
9//! When `HAS_*` is false, the method is NOT installed with R, so R uses its own default behavior.
10//!
11//! ## Required Methods by Type
12//!
13//! | Type | Required Methods |
14//! |------|------------------|
15//! | All | `length` |
16//! | ALTSTRING | `length` + `elt` |
17//! | ALTLIST | `length` + `elt` |
18//! | Numeric types | `length` + (`elt` OR `dataptr` via HAS_*) |
19
20use crate::ffi::{R_xlen_t, Rcomplex, SEXP, SEXPTYPE};
21use core::ffi::c_void;
22
23// region: ALTREP GUARD MODE
24
25/// Controls the panic/error guard used around ALTREP trampoline callbacks.
26///
27/// Each mode trades off safety vs performance:
28///
29/// - [`Unsafe`](AltrepGuard::Unsafe): No protection. If the callback panics,
30/// behavior is undefined (unwinding through C frames). Use only for trivial
31/// callbacks that cannot panic.
32///
33/// - [`RustUnwind`](AltrepGuard::RustUnwind): Wraps in `catch_unwind`, converting
34/// Rust panics to R errors. This is the **default** and safe for all pure-Rust
35/// callbacks. Overhead: ~1-2ns per call.
36///
37/// - [`RUnwind`](AltrepGuard::RUnwind): Wraps in `R_UnwindProtect`, catching both
38/// Rust panics and R `longjmp` errors. Use when ALTREP callbacks invoke R API
39/// functions that might error (e.g., `Rf_allocVector`, `Rf_eval`).
40///
41/// The guard is selected via the `const GUARD` associated constant on the [`Altrep`]
42/// trait. Since it is a const, the compiler eliminates dead branches at
43/// monomorphization time — zero runtime overhead for the chosen mode.
44#[derive(Clone, Copy, PartialEq, Eq, Debug)]
45pub enum AltrepGuard {
46 /// No protection. Fastest, but if the callback panics, behavior is undefined.
47 Unsafe,
48 /// `catch_unwind` — catches Rust panics, converts to R errors. Default.
49 RustUnwind,
50 /// `with_r_unwind_protect` — catches both Rust panics and R longjmps.
51 /// Use when ALTREP callbacks invoke R API functions that might error.
52 RUnwind,
53}
54// endregion
55
56// region: ALTREP BASE
57
58/// Base ALTREP methods.
59///
60/// `length` is REQUIRED (no default). All other methods are optional with HAS_* gating.
61pub trait Altrep {
62 /// The guard mode for all ALTREP trampolines on this type.
63 ///
64 /// Defaults to [`AltrepGuard::RUnwind`] which catches both Rust panics and
65 /// R longjmps via `R_UnwindProtect`. This is safe for all callbacks, including
66 /// those that call R API functions (e.g., `Rf_mkCharLenCE`, `Rf_ScalarReal`,
67 /// `into_sexp` in serialization).
68 ///
69 /// Override to [`AltrepGuard::RustUnwind`] if your callbacks never call R APIs
70 /// and you want to save ~2ns per call, or [`AltrepGuard::Unsafe`] for trivial
71 /// callbacks that cannot panic.
72 const GUARD: AltrepGuard = AltrepGuard::RUnwind;
73
74 // --- REQUIRED ---
75 /// Returns the length of the ALTREP vector.
76 /// This is REQUIRED - R cannot determine vector length without it.
77 fn length(x: SEXP) -> R_xlen_t;
78
79 // --- OPTIONAL: Serialization ---
80 /// Set to `true` to register [`serialized_state`](Self::serialized_state).
81 const HAS_SERIALIZED_STATE: bool = false;
82 /// Return serialization state.
83 fn serialized_state(_x: SEXP) -> SEXP {
84 unreachable!("HAS_SERIALIZED_STATE = false")
85 }
86
87 /// Set to `true` to register [`unserialize`](Self::unserialize).
88 const HAS_UNSERIALIZE: bool = false;
89 /// Reconstruct ALTREP from serialized state.
90 fn unserialize(_class: SEXP, _state: SEXP) -> SEXP {
91 unreachable!("HAS_UNSERIALIZE = false")
92 }
93
94 /// Set to `true` to register [`unserialize_ex`](Self::unserialize_ex).
95 const HAS_UNSERIALIZE_EX: bool = false;
96 /// Extended unserialization with attributes.
97 fn unserialize_ex(_class: SEXP, _state: SEXP, _attr: SEXP, _objf: i32, _levs: i32) -> SEXP {
98 unreachable!("HAS_UNSERIALIZE_EX = false")
99 }
100
101 // --- OPTIONAL: Duplication ---
102 /// Set to `true` to register [`duplicate`](Self::duplicate).
103 const HAS_DUPLICATE: bool = false;
104 /// Duplicate the ALTREP object.
105 fn duplicate(_x: SEXP, _deep: bool) -> SEXP {
106 unreachable!("HAS_DUPLICATE = false")
107 }
108
109 /// Set to `true` to register [`duplicate_ex`](Self::duplicate_ex).
110 const HAS_DUPLICATE_EX: bool = false;
111 /// Extended duplication.
112 fn duplicate_ex(_x: SEXP, _deep: bool) -> SEXP {
113 unreachable!("HAS_DUPLICATE_EX = false")
114 }
115
116 // --- OPTIONAL: Coercion ---
117 /// Set to `true` to register [`coerce`](Self::coerce).
118 const HAS_COERCE: bool = false;
119 /// Coerce to another type.
120 fn coerce(_x: SEXP, _to_type: SEXPTYPE) -> SEXP {
121 unreachable!("HAS_COERCE = false")
122 }
123
124 // --- OPTIONAL: Inspection ---
125 /// Set to `true` to register [`inspect`](Self::inspect).
126 const HAS_INSPECT: bool = false;
127 /// Custom inspection for `.Internal(inspect())`.
128 fn inspect(
129 _x: SEXP,
130 _pre: i32,
131 _deep: i32,
132 _pvec: i32,
133 _inspect_subtree: Option<unsafe extern "C-unwind" fn(SEXP, i32, i32, i32)>,
134 ) -> bool {
135 unreachable!("HAS_INSPECT = false")
136 }
137}
138// endregion
139
140// region: ALTVEC - Vector-level methods (extends Altrep)
141
142/// Vector-level methods.
143///
144/// All methods are optional with HAS_* gating.
145pub trait AltVec: Altrep {
146 /// Set to `true` to register [`dataptr`](Self::dataptr).
147 const HAS_DATAPTR: bool = false;
148 /// Get raw data pointer.
149 fn dataptr(_x: SEXP, _writable: bool) -> *mut c_void {
150 unreachable!("HAS_DATAPTR = false")
151 }
152
153 /// Set to `true` to register [`dataptr_or_null`](Self::dataptr_or_null).
154 const HAS_DATAPTR_OR_NULL: bool = false;
155 /// Get data pointer without forcing materialization.
156 fn dataptr_or_null(_x: SEXP) -> *const c_void {
157 unreachable!("HAS_DATAPTR_OR_NULL = false")
158 }
159
160 /// Set to `true` to register [`extract_subset`](Self::extract_subset).
161 const HAS_EXTRACT_SUBSET: bool = false;
162 /// Optimized subsetting.
163 fn extract_subset(_x: SEXP, _indx: SEXP, _call: SEXP) -> SEXP {
164 unreachable!("HAS_EXTRACT_SUBSET = false")
165 }
166}
167// endregion
168
169// region: ALTINTEGER - Integer vector methods
170
171/// Integer vector methods.
172///
173/// For ALTINTEGER, you must provide EITHER:
174/// - `HAS_ELT = true` with `elt()` implementation, OR
175/// - `HAS_DATAPTR = true` with `dataptr()` implementation
176///
177/// If neither is provided, R will error at runtime when accessing elements.
178pub trait AltInteger: AltVec {
179 /// Set to `true` to register [`elt`](Self::elt).
180 const HAS_ELT: bool = false;
181 /// Get element at index.
182 fn elt(_x: SEXP, _i: R_xlen_t) -> i32 {
183 unreachable!("HAS_ELT = false")
184 }
185
186 /// Set to `true` to register [`get_region`](Self::get_region).
187 const HAS_GET_REGION: bool = false;
188 /// Bulk read elements into buffer.
189 fn get_region(_x: SEXP, _i: R_xlen_t, _n: R_xlen_t, _buf: &mut [i32]) -> R_xlen_t {
190 unreachable!("HAS_GET_REGION = false")
191 }
192
193 /// Set to `true` to register [`is_sorted`](Self::is_sorted).
194 const HAS_IS_SORTED: bool = false;
195 /// Sortedness hint.
196 fn is_sorted(_x: SEXP) -> i32 {
197 unreachable!("HAS_IS_SORTED = false")
198 }
199
200 /// Set to `true` to register [`no_na`](Self::no_na).
201 const HAS_NO_NA: bool = false;
202 /// NA-free hint.
203 fn no_na(_x: SEXP) -> i32 {
204 unreachable!("HAS_NO_NA = false")
205 }
206
207 /// Set to `true` to register [`sum`](Self::sum).
208 const HAS_SUM: bool = false;
209 /// Optimized sum.
210 fn sum(_x: SEXP, _narm: bool) -> SEXP {
211 unreachable!("HAS_SUM = false")
212 }
213
214 /// Set to `true` to register [`min`](Self::min).
215 const HAS_MIN: bool = false;
216 /// Optimized min.
217 fn min(_x: SEXP, _narm: bool) -> SEXP {
218 unreachable!("HAS_MIN = false")
219 }
220
221 /// Set to `true` to register [`max`](Self::max).
222 const HAS_MAX: bool = false;
223 /// Optimized max.
224 fn max(_x: SEXP, _narm: bool) -> SEXP {
225 unreachable!("HAS_MAX = false")
226 }
227}
228// endregion
229
230// region: ALTREAL - Real (double) vector methods
231
232/// Real vector methods.
233pub trait AltReal: AltVec {
234 /// Set to `true` to register [`elt`](Self::elt).
235 const HAS_ELT: bool = false;
236 /// Get element at index.
237 fn elt(_x: SEXP, _i: R_xlen_t) -> f64 {
238 unreachable!("HAS_ELT = false")
239 }
240
241 /// Set to `true` to register [`get_region`](Self::get_region).
242 const HAS_GET_REGION: bool = false;
243 /// Bulk read elements into buffer.
244 fn get_region(_x: SEXP, _i: R_xlen_t, _n: R_xlen_t, _buf: &mut [f64]) -> R_xlen_t {
245 unreachable!("HAS_GET_REGION = false")
246 }
247
248 /// Set to `true` to register [`is_sorted`](Self::is_sorted).
249 const HAS_IS_SORTED: bool = false;
250 /// Sortedness hint.
251 fn is_sorted(_x: SEXP) -> i32 {
252 unreachable!("HAS_IS_SORTED = false")
253 }
254
255 /// Set to `true` to register [`no_na`](Self::no_na).
256 const HAS_NO_NA: bool = false;
257 /// NA-free hint.
258 fn no_na(_x: SEXP) -> i32 {
259 unreachable!("HAS_NO_NA = false")
260 }
261
262 /// Set to `true` to register [`sum`](Self::sum).
263 const HAS_SUM: bool = false;
264 /// Optimized sum.
265 fn sum(_x: SEXP, _narm: bool) -> SEXP {
266 unreachable!("HAS_SUM = false")
267 }
268
269 /// Set to `true` to register [`min`](Self::min).
270 const HAS_MIN: bool = false;
271 /// Optimized min.
272 fn min(_x: SEXP, _narm: bool) -> SEXP {
273 unreachable!("HAS_MIN = false")
274 }
275
276 /// Set to `true` to register [`max`](Self::max).
277 const HAS_MAX: bool = false;
278 /// Optimized max.
279 fn max(_x: SEXP, _narm: bool) -> SEXP {
280 unreachable!("HAS_MAX = false")
281 }
282}
283// endregion
284
285// region: ALTLOGICAL - Logical vector methods
286
287/// Logical vector methods.
288pub trait AltLogical: AltVec {
289 /// Set to `true` to register [`elt`](Self::elt).
290 const HAS_ELT: bool = false;
291 /// Returns i32: 0=FALSE, 1=TRUE, NA_LOGICAL=NA
292 fn elt(_x: SEXP, _i: R_xlen_t) -> i32 {
293 unreachable!("HAS_ELT = false")
294 }
295
296 /// Set to `true` to register [`get_region`](Self::get_region).
297 const HAS_GET_REGION: bool = false;
298 /// Bulk read elements into buffer.
299 fn get_region(_x: SEXP, _i: R_xlen_t, _n: R_xlen_t, _buf: &mut [i32]) -> R_xlen_t {
300 unreachable!("HAS_GET_REGION = false")
301 }
302
303 /// Set to `true` to register [`is_sorted`](Self::is_sorted).
304 const HAS_IS_SORTED: bool = false;
305 /// Sortedness hint.
306 fn is_sorted(_x: SEXP) -> i32 {
307 unreachable!("HAS_IS_SORTED = false")
308 }
309
310 /// Set to `true` to register [`no_na`](Self::no_na).
311 const HAS_NO_NA: bool = false;
312 /// NA-free hint.
313 fn no_na(_x: SEXP) -> i32 {
314 unreachable!("HAS_NO_NA = false")
315 }
316
317 /// Set to `true` to register [`sum`](Self::sum).
318 const HAS_SUM: bool = false;
319 /// Sum for logical = count of TRUE values.
320 fn sum(_x: SEXP, _narm: bool) -> SEXP {
321 unreachable!("HAS_SUM = false")
322 }
323 // Note: R's ALTREP API does not expose min/max for logical vectors
324}
325// endregion
326
327// region: ALTRAW - Raw (byte) vector methods
328
329/// Raw vector methods.
330pub trait AltRaw: AltVec {
331 /// Set to `true` to register [`elt`](Self::elt).
332 const HAS_ELT: bool = false;
333 /// Get element at index.
334 fn elt(_x: SEXP, _i: R_xlen_t) -> u8 {
335 unreachable!("HAS_ELT = false")
336 }
337
338 /// Set to `true` to register [`get_region`](Self::get_region).
339 const HAS_GET_REGION: bool = false;
340 /// Bulk read elements into buffer.
341 fn get_region(_x: SEXP, _i: R_xlen_t, _n: R_xlen_t, _buf: &mut [u8]) -> R_xlen_t {
342 unreachable!("HAS_GET_REGION = false")
343 }
344}
345// endregion
346
347// region: ALTCOMPLEX - Complex vector methods
348
349/// Complex vector methods.
350pub trait AltComplex: AltVec {
351 /// Set to `true` to register [`elt`](Self::elt).
352 const HAS_ELT: bool = false;
353 /// Get element at index.
354 fn elt(_x: SEXP, _i: R_xlen_t) -> Rcomplex {
355 unreachable!("HAS_ELT = false")
356 }
357
358 /// Set to `true` to register [`get_region`](Self::get_region).
359 const HAS_GET_REGION: bool = false;
360 /// Bulk read elements into buffer.
361 fn get_region(_x: SEXP, _i: R_xlen_t, _n: R_xlen_t, _buf: &mut [Rcomplex]) -> R_xlen_t {
362 unreachable!("HAS_GET_REGION = false")
363 }
364}
365// endregion
366
367// region: ALTSTRING - String vector methods
368
369/// String vector methods.
370///
371/// **REQUIRED**: `elt` must be implemented (no default).
372/// R will error with "No Elt method found" if you don't provide it.
373pub trait AltString: AltVec {
374 // --- REQUIRED for ALTSTRING ---
375 /// Get string element at index. Returns CHARSXP.
376 /// This is REQUIRED for ALTSTRING - there is no default.
377 fn elt(x: SEXP, i: R_xlen_t) -> SEXP;
378
379 // --- OPTIONAL ---
380 /// Set to `true` to register [`set_elt`](Self::set_elt).
381 const HAS_SET_ELT: bool = false;
382 /// Set element (for mutable strings).
383 fn set_elt(_x: SEXP, _i: R_xlen_t, _v: SEXP) {
384 unreachable!("HAS_SET_ELT = false")
385 }
386
387 /// Set to `true` to register [`is_sorted`](Self::is_sorted).
388 const HAS_IS_SORTED: bool = false;
389 /// Sortedness hint.
390 fn is_sorted(_x: SEXP) -> i32 {
391 unreachable!("HAS_IS_SORTED = false")
392 }
393
394 /// Set to `true` to register [`no_na`](Self::no_na).
395 const HAS_NO_NA: bool = false;
396 /// NA-free hint.
397 fn no_na(_x: SEXP) -> i32 {
398 unreachable!("HAS_NO_NA = false")
399 }
400}
401// endregion
402
403// region: ALTLIST - List (VECSXP) methods
404
405/// List vector methods.
406///
407/// **REQUIRED**: `elt` must be implemented (no default).
408/// R will error with "must provide an Elt method" if you don't provide it.
409pub trait AltList: AltVec {
410 // --- REQUIRED for ALTLIST ---
411 /// Get list element at index. Returns any SEXP.
412 /// This is REQUIRED for ALTLIST - there is no default.
413 fn elt(x: SEXP, i: R_xlen_t) -> SEXP;
414
415 // --- OPTIONAL ---
416 /// Set to `true` to register [`set_elt`](Self::set_elt).
417 const HAS_SET_ELT: bool = false;
418 /// Set element (for mutable lists).
419 fn set_elt(_x: SEXP, _i: R_xlen_t, _v: SEXP) {
420 unreachable!("HAS_SET_ELT = false")
421 }
422}
423// endregion
424
425// region: Constants
426
427/// Unknown sortedness value (INT_MIN in R).
428pub const UNKNOWN_SORTEDNESS: i32 = i32::MIN;
429
430/// Known to be unsorted (`KNOWN_UNSORTED` in R).
431pub const KNOWN_UNSORTED: i32 = 0;
432
433/// Sorted in increasing order, possibly with ties (`SORTED_INCR` in R).
434pub const SORTED_INCR: i32 = 1;
435
436/// Sorted in decreasing order, possibly with ties (`SORTED_DECR` in R).
437pub const SORTED_DECR: i32 = -1;
438
439/// Sorted in increasing order, with NAs first (`SORTED_INCR_NA_1ST` in R).
440pub const SORTED_INCR_NA_1ST: i32 = 2;
441
442/// Sorted in decreasing order, with NAs first (`SORTED_DECR_NA_1ST` in R).
443pub const SORTED_DECR_NA_1ST: i32 = -2;
444/// NA value for integers.
445pub const NA_INTEGER: i32 = i32::MIN;
446/// NA value for logical (same as integer in R).
447pub const NA_LOGICAL: i32 = i32::MIN;
448/// NA value for reals (IEEE NaN with R's NA payload).
449pub const NA_REAL: f64 = f64::from_bits(0x7FF0_0000_0000_07A2);
450// endregion