Skip to main content

miniextendr_api/
sexp_ext.rs

1//! `SexpExt` — the ergonomic extension trait on `SEXP`.
2//!
3//! Provides safe(r) accessors and type checks. This is the actual user-facing
4//! API surface most callers reach for; it's re-exported from
5//! [`crate::prelude`].
6
7use crate::SEXP;
8use crate::sexp_types::{R_xlen_t, RNativeType, Rboolean, Rcomplex, SEXPTYPE};
9// Pull in everything raw from sys. Type re-exports (SEXP, SEXPTYPE, etc.)
10// that `sys` itself promotes are identical items, so glob import is safe;
11// we still bring SEXP/SEXPTYPE in via crate root above so the file reads
12// naturally.
13use crate::sys::{
14    ALTREP, CAR, CAR_unchecked, CDR, CDR_unchecked, COMPLEX_ELT, DATAPTR_RO, DATAPTR_RO_unchecked,
15    INTEGER_ELT, LOGICAL_ELT, PRINTNAME, R_CHAR, R_CHAR_unchecked, R_ClassSymbol, R_DimNamesSymbol,
16    R_DimSymbol, R_LevelsSymbol, R_NaString, R_NamesSymbol, R_NilValue, R_RowNamesSymbol, RAW_ELT,
17    REAL_ELT, Rf_asChar, Rf_asInteger, Rf_asLogical, Rf_asReal, Rf_classgets, Rf_coerceVector,
18    Rf_cons, Rf_cons_unchecked, Rf_dimgets, Rf_dimnamesgets, Rf_duplicate, Rf_getAttrib,
19    Rf_getAttrib_unchecked, Rf_inherits, Rf_isArray, Rf_isFactor, Rf_isFunction, Rf_isList,
20    Rf_isMatrix, Rf_isObject, Rf_isS4, Rf_lcons, Rf_namesgets, Rf_setAttrib,
21    Rf_setAttrib_unchecked, Rf_shallow_duplicate, Rf_xlength, Rf_xlength_unchecked, Rf_xlengthgets,
22    SET_COMPLEX_ELT, SET_INTEGER_ELT, SET_LOGICAL_ELT, SET_RAW_ELT, SET_REAL_ELT, SET_STRING_ELT,
23    SET_STRING_ELT_unchecked, SET_TAG, SET_TAG_unchecked, SET_VECTOR_ELT, SET_VECTOR_ELT_unchecked,
24    SETCAR, SETCAR_unchecked, SETCDR, SETCDR_unchecked, STRING_ELT, STRING_ELT_unchecked, TAG,
25    TYPEOF, VECTOR_ELT, VECTOR_ELT_unchecked,
26};
27
28/// Extension trait for SEXP providing safe(r) accessors and type checking.
29///
30/// This trait provides idiomatic Rust methods for working with SEXPs,
31/// equivalent to R's inline macros and type checking functions.
32pub trait SexpExt {
33    /// Get the type of this SEXP.
34    ///
35    /// Equivalent to `TYPEOF(x)` macro.
36    ///
37    /// # Safety
38    ///
39    /// The SEXP must be valid (not null and not freed).
40    #[must_use]
41    fn type_of(&self) -> SEXPTYPE;
42
43    /// Check if this SEXP is null or R_NilValue.
44    #[must_use]
45    fn is_null_or_nil(&self) -> bool;
46
47    /// Get the length of this SEXP as `usize`.
48    ///
49    /// # Safety
50    ///
51    /// The SEXP must be valid.
52    #[must_use]
53    fn len(&self) -> usize;
54
55    /// Get the length as `R_xlen_t`.
56    ///
57    /// # Safety
58    ///
59    /// The SEXP must be valid.
60    #[must_use]
61    fn xlength(&self) -> R_xlen_t;
62
63    /// Get the length as `R_xlen_t` without thread checks.
64    ///
65    /// # Safety
66    ///
67    /// Must be called from R's main thread. No debug assertions.
68    #[must_use]
69    unsafe fn xlength_unchecked(&self) -> R_xlen_t;
70
71    /// Get the length without thread checks.
72    ///
73    /// # Safety
74    ///
75    /// Must be called from R's main thread. No debug assertions.
76    #[must_use]
77    unsafe fn len_unchecked(&self) -> usize;
78
79    /// Get a slice view of this SEXP's data.
80    ///
81    /// # Safety
82    ///
83    /// - The SEXP must be valid and of the correct type for `T`
84    /// - The SEXP must be protected from R's garbage collector for the entire
85    ///   duration the returned slice is used. This typically means the SEXP must
86    ///   be either:
87    ///   - An argument to a `.Call` function (protected by R's calling convention)
88    ///   - Explicitly protected via `PROTECT`/`UNPROTECT` or `R_PreserveObject`
89    ///   - Part of a protected container (e.g., element of a protected list)
90    /// - The returned slice has `'static` lifetime for API convenience, but this
91    ///   is a lie - the actual lifetime is tied to the SEXP's protection status.
92    ///   Holding the slice after the SEXP is unprotected is undefined behavior.
93    unsafe fn as_slice<T: RNativeType>(&self) -> &'static [T];
94
95    /// Get a slice view without thread checks.
96    ///
97    /// # Safety
98    ///
99    /// - All safety requirements of [`as_slice`](Self::as_slice) apply
100    /// - Additionally, must be called from R's main thread (no debug assertions)
101    unsafe fn as_slice_unchecked<T: RNativeType>(&self) -> &'static [T];
102
103    /// Get a mutable slice view of this SEXP's data.
104    ///
105    /// # Safety
106    ///
107    /// - All safety requirements of [`as_slice`](Self::as_slice) apply.
108    /// - The caller must ensure **exclusive access**: no other `&[T]` or `&mut [T]`
109    ///   slices derived from this SEXP may exist simultaneously. Multiple calls to
110    ///   `as_mut_slice` on the same SEXP without dropping the previous slice is UB.
111    /// - The SEXP must not be shared (ALTREP or NAMED > 0 objects may alias).
112    unsafe fn as_mut_slice<T: RNativeType>(&self) -> &'static mut [T];
113
114    // Type checking methods (equivalent to R's type check macros)
115
116    /// Check if this SEXP is an integer vector (INTSXP).
117    #[must_use]
118    fn is_integer(&self) -> bool;
119
120    /// Check if this SEXP is a real/numeric vector (REALSXP).
121    #[must_use]
122    fn is_real(&self) -> bool;
123
124    /// Check if this SEXP is a logical vector (LGLSXP).
125    #[must_use]
126    fn is_logical(&self) -> bool;
127
128    /// Check if this SEXP is a character/string vector (STRSXP).
129    #[must_use]
130    fn is_character(&self) -> bool;
131
132    /// Check if this SEXP is a raw vector (RAWSXP).
133    #[must_use]
134    fn is_raw(&self) -> bool;
135
136    /// Check if this SEXP is a complex vector (CPLXSXP).
137    #[must_use]
138    fn is_complex(&self) -> bool;
139
140    /// Check if this SEXP is a list/generic vector (VECSXP).
141    #[must_use]
142    fn is_list(&self) -> bool;
143
144    /// Check if this SEXP is an external pointer (EXTPTRSXP).
145    #[must_use]
146    fn is_external_ptr(&self) -> bool;
147
148    /// Check if this SEXP is an environment (ENVSXP).
149    #[must_use]
150    fn is_environment(&self) -> bool;
151
152    /// Check if this SEXP is a symbol (SYMSXP).
153    #[must_use]
154    fn is_symbol(&self) -> bool;
155
156    /// Check if this SEXP is a language object (LANGSXP).
157    #[must_use]
158    fn is_language(&self) -> bool;
159
160    /// Check if this SEXP is an ALTREP object.
161    ///
162    /// Equivalent to R's `ALTREP(x)` macro.
163    #[must_use]
164    fn is_altrep(&self) -> bool;
165
166    /// Check if this `SEXP` contains any elements.
167    #[must_use]
168    fn is_empty(&self) -> bool;
169
170    /// Check if this SEXP is R's `NULL` (NILSXP).
171    #[must_use]
172    fn is_nil(&self) -> bool;
173
174    /// Check if this SEXP is a factor.
175    ///
176    /// Equivalent to R's `Rf_isFactor(x)`.
177    #[must_use]
178    fn is_factor(&self) -> bool;
179
180    /// Check if this SEXP is a pairlist (LISTSXP or NILSXP).
181    ///
182    /// Equivalent to R's `Rf_isList(x)`.
183    #[must_use]
184    fn is_pair_list(&self) -> bool;
185
186    /// Check if this SEXP is a matrix.
187    ///
188    /// Equivalent to R's `Rf_isMatrix(x)`.
189    #[must_use]
190    fn is_matrix(&self) -> bool;
191
192    /// Check if this SEXP is an array.
193    ///
194    /// Equivalent to R's `Rf_isArray(x)`.
195    #[must_use]
196    fn is_array(&self) -> bool;
197
198    /// Check if this SEXP is a function (closure, builtin, or special).
199    ///
200    /// Equivalent to R's `Rf_isFunction(x)`.
201    #[must_use]
202    fn is_function(&self) -> bool;
203
204    /// Check if this SEXP is an S4 object.
205    ///
206    /// Equivalent to R's `Rf_isS4(x)`.
207    #[must_use]
208    fn is_s4(&self) -> bool;
209
210    /// Check if this SEXP is a data.frame.
211    ///
212    /// Equivalent to R's `Rf_isDataFrame(x)`.
213    #[must_use]
214    fn is_data_frame(&self) -> bool;
215
216    /// Check if this SEXP is a numeric type (integer, logical, or real, excluding factors).
217    ///
218    /// Equivalent to R's `Rf_isNumeric(x)`.
219    #[must_use]
220    fn is_numeric(&self) -> bool;
221
222    /// Check if this SEXP is a number type (numeric or complex).
223    ///
224    /// Equivalent to R's `Rf_isNumber(x)`.
225    #[must_use]
226    fn is_number(&self) -> bool;
227
228    /// Check if this SEXP is an atomic vector.
229    ///
230    /// Returns true for logical, integer, real, complex, character, and raw vectors.
231    #[must_use]
232    fn is_vector_atomic(&self) -> bool;
233
234    /// Check if this SEXP is a vector list (VECSXP or EXPRSXP).
235    #[must_use]
236    fn is_vector_list(&self) -> bool;
237
238    /// Check if this SEXP is a vector (atomic vector or list).
239    #[must_use]
240    fn is_vector(&self) -> bool;
241
242    /// Check if this SEXP is an R "object" (has a class attribute).
243    #[must_use]
244    fn is_object(&self) -> bool;
245
246    // region: Coercion and scalar extraction
247
248    /// Coerce this SEXP to the given type, returning a new SEXP.
249    ///
250    /// The result is guaranteed to have the requested SEXPTYPE.
251    /// Equivalent to R's `Rf_coerceVector(x, target)`.
252    #[must_use]
253    fn coerce(&self, target: SEXPTYPE) -> SEXP;
254
255    /// Extract a scalar logical value.
256    ///
257    /// Returns `None` for `NA`. Coerces non-logical inputs.
258    /// Equivalent to R's `Rf_asLogical(x)`.
259    #[must_use]
260    fn as_logical(&self) -> Option<bool>;
261
262    /// Extract a scalar integer value.
263    ///
264    /// Returns `None` for `NA_integer_`. Coerces non-integer inputs.
265    /// Equivalent to R's `Rf_asInteger(x)`.
266    #[must_use]
267    fn as_integer(&self) -> Option<i32>;
268
269    /// Extract a scalar real value.
270    ///
271    /// Returns `None` for `NA_real_` (NaN). Coerces non-real inputs.
272    /// Equivalent to R's `Rf_asReal(x)`.
273    #[must_use]
274    fn as_real(&self) -> Option<f64>;
275
276    /// Extract a scalar CHARSXP from this SEXP.
277    ///
278    /// The result is guaranteed to be a CHARSXP.
279    /// Equivalent to R's `Rf_asChar(x)`.
280    #[must_use]
281    fn as_char(&self) -> SEXP;
282
283    // endregion
284
285    // region: Attribute access
286
287    /// Get an attribute by symbol.
288    #[must_use]
289    fn get_attr(&self, name: SEXP) -> SEXP;
290
291    /// Get an attribute by symbol, returning `None` for `R_NilValue`.
292    #[must_use]
293    fn get_attr_opt(&self, name: SEXP) -> Option<SEXP> {
294        let attr = self.get_attr(name);
295        if attr.is_nil() { None } else { Some(attr) }
296    }
297
298    /// Set an attribute by symbol.
299    fn set_attr(&self, name: SEXP, val: SEXP);
300
301    /// Get the `names` attribute.
302    #[must_use]
303    fn get_names(&self) -> SEXP;
304
305    /// Set the `names` attribute.
306    fn set_names(&self, names: SEXP);
307
308    /// Get the `class` attribute.
309    #[must_use]
310    fn get_class(&self) -> SEXP;
311
312    /// Set the `class` attribute.
313    fn set_class(&self, class: SEXP);
314
315    /// Get the `dim` attribute.
316    #[must_use]
317    fn get_dim(&self) -> SEXP;
318
319    /// Set the `dim` attribute.
320    fn set_dim(&self, dim: SEXP);
321
322    /// Get the `dimnames` attribute.
323    #[must_use]
324    fn get_dimnames(&self) -> SEXP;
325
326    /// Set the `dimnames` attribute.
327    fn set_dimnames(&self, dimnames: SEXP);
328
329    /// Get the `levels` attribute (factors).
330    #[must_use]
331    fn get_levels(&self) -> SEXP;
332
333    /// Set the `levels` attribute (factors).
334    fn set_levels(&self, levels: SEXP);
335
336    /// Get the `row.names` attribute.
337    #[must_use]
338    fn get_row_names(&self) -> SEXP;
339
340    /// Set the `row.names` attribute.
341    fn set_row_names(&self, row_names: SEXP);
342
343    /// Check if this SEXP inherits from a class.
344    ///
345    /// Equivalent to R's `inherits(x, "class_name")`.
346    #[must_use]
347    fn inherits_class(&self, class: &std::ffi::CStr) -> bool;
348
349    // endregion
350
351    // region: String element access
352
353    /// Get the i-th CHARSXP element from a STRSXP.
354    ///
355    /// Equivalent to R's `STRING_ELT(x, i)`.
356    #[must_use]
357    fn string_elt(&self, i: isize) -> SEXP;
358
359    /// Get the i-th string element as `Option<&str>`.
360    ///
361    /// Returns `None` for `NA_character_`. The returned `&str` borrows from R's
362    /// internal string cache (CHARSXP global pool) and is valid as long as the
363    /// parent STRSXP is protected from GC. The lifetime is tied to `&self` by
364    /// the borrow checker, but the true validity depends on GC protection —
365    /// do not hold the `&str` across allocation boundaries without ensuring
366    /// the SEXP remains protected.
367    #[must_use]
368    fn string_elt_str(&self, i: isize) -> Option<&str>;
369
370    /// Set the i-th CHARSXP element of a STRSXP.
371    ///
372    /// Equivalent to R's `SET_STRING_ELT(x, i, v)`.
373    fn set_string_elt(&self, i: isize, charsxp: SEXP);
374
375    /// Check if this CHARSXP is `NA_character_`.
376    #[must_use]
377    fn is_na_string(&self) -> bool;
378
379    // endregion
380
381    // region: List element access
382
383    /// Get the i-th element of a VECSXP (generic vector / list).
384    ///
385    /// Equivalent to R's `VECTOR_ELT(x, i)`.
386    #[must_use]
387    fn vector_elt(&self, i: isize) -> SEXP;
388
389    /// Set the i-th element of a VECSXP.
390    ///
391    /// Equivalent to R's `SET_VECTOR_ELT(x, i, v)`.
392    fn set_vector_elt(&self, i: isize, val: SEXP);
393
394    // endregion
395
396    // region: Typed single-element access
397
398    /// Get the i-th integer element.
399    #[must_use]
400    fn integer_elt(&self, i: isize) -> i32;
401    /// Get the i-th real element.
402    #[must_use]
403    fn real_elt(&self, i: isize) -> f64;
404    /// Get the i-th logical element (raw i32: 0/1/NA_LOGICAL).
405    #[must_use]
406    fn logical_elt(&self, i: isize) -> i32;
407    /// Get the i-th complex element.
408    #[must_use]
409    fn complex_elt(&self, i: isize) -> Rcomplex;
410    /// Get the i-th raw element.
411    #[must_use]
412    fn raw_elt(&self, i: isize) -> u8;
413
414    /// Set the i-th integer element.
415    fn set_integer_elt(&self, i: isize, v: i32);
416    /// Set the i-th real element.
417    fn set_real_elt(&self, i: isize, v: f64);
418    /// Set the i-th logical element (raw i32: 0/1/NA_LOGICAL).
419    fn set_logical_elt(&self, i: isize, v: i32);
420    /// Set the i-th complex element.
421    fn set_complex_elt(&self, i: isize, v: Rcomplex);
422    /// Set the i-th raw element.
423    fn set_raw_elt(&self, i: isize, v: u8);
424
425    // endregion
426
427    // region: Symbol and CHARSXP access
428
429    /// Get the print name (CHARSXP) of a symbol (SYMSXP).
430    ///
431    /// # Safety
432    ///
433    /// The SEXP must be a valid SYMSXP.
434    #[must_use]
435    fn printname(&self) -> SEXP;
436
437    /// Get the C string pointer from a CHARSXP.
438    ///
439    /// The returned pointer is valid as long as the CHARSXP is protected.
440    ///
441    /// # Safety
442    ///
443    /// The SEXP must be a valid CHARSXP.
444    #[must_use]
445    fn r_char(&self) -> *const ::std::os::raw::c_char;
446
447    /// Get a `&str` from a CHARSXP. Returns `None` for `NA_character_`.
448    #[must_use]
449    fn r_char_str(&self) -> Option<&str>;
450
451    // endregion
452
453    // region: Vector resizing
454
455    /// Resize a vector to a new length, returning a (possibly new) SEXP.
456    ///
457    /// If the new length is shorter, elements are truncated.
458    /// If longer, new elements are filled with NA/NULL.
459    /// Equivalent to R's `Rf_xlengthgets(x, newlen)`.
460    #[must_use]
461    fn resize(&self, newlen: R_xlen_t) -> SEXP;
462
463    // endregion
464
465    // region: Duplication
466
467    /// Deep-copy this SEXP. Equivalent to R's `Rf_duplicate(x)`.
468    #[must_use]
469    fn duplicate(&self) -> SEXP;
470
471    /// Shallow-copy this SEXP. Equivalent to R's `Rf_shallow_duplicate(x)`.
472    #[must_use]
473    fn shallow_duplicate(&self) -> SEXP;
474
475    // endregion
476
477    // region: Unchecked variants (bypass thread-check, for perf-critical paths)
478
479    /// Get the i-th CHARSXP from a STRSXP. No thread check.
480    ///
481    /// # Safety
482    /// Must be called from R's main thread.
483    #[must_use]
484    unsafe fn string_elt_unchecked(&self, i: isize) -> SEXP;
485    /// Set the i-th CHARSXP of a STRSXP. No thread check.
486    ///
487    /// # Safety
488    /// Must be called from R's main thread.
489    unsafe fn set_string_elt_unchecked(&self, i: isize, charsxp: SEXP);
490    /// Get the i-th element of a VECSXP. No thread check.
491    ///
492    /// # Safety
493    /// Must be called from R's main thread.
494    #[must_use]
495    unsafe fn vector_elt_unchecked(&self, i: isize) -> SEXP;
496    /// Set the i-th element of a VECSXP. No thread check.
497    ///
498    /// # Safety
499    /// Must be called from R's main thread.
500    unsafe fn set_vector_elt_unchecked(&self, i: isize, val: SEXP);
501    /// Get an attribute by symbol. No thread check.
502    ///
503    /// # Safety
504    /// Must be called from R's main thread.
505    #[must_use]
506    unsafe fn get_attr_unchecked(&self, name: SEXP) -> SEXP;
507    /// Set an attribute by symbol. No thread check.
508    ///
509    /// # Safety
510    /// Must be called from R's main thread.
511    unsafe fn set_attr_unchecked(&self, name: SEXP, val: SEXP);
512
513    /// Get C string pointer from a CHARSXP. No thread check.
514    ///
515    /// # Safety
516    /// Must be called from R's main thread. The SEXP must be a valid CHARSXP.
517    #[must_use]
518    unsafe fn r_char_unchecked(&self) -> *const ::std::os::raw::c_char;
519
520    // endregion
521}
522
523impl SexpExt for SEXP {
524    #[inline]
525    fn type_of(&self) -> SEXPTYPE {
526        unsafe { TYPEOF(*self) }
527    }
528
529    #[inline]
530    fn is_null_or_nil(&self) -> bool {
531        self.is_null() || std::ptr::addr_eq(self.0, unsafe { R_NilValue.0 })
532    }
533
534    #[inline]
535    fn len(&self) -> usize {
536        unsafe { Rf_xlength(*self) as usize }
537    }
538
539    #[inline]
540    fn xlength(&self) -> R_xlen_t {
541        unsafe { Rf_xlength(*self) }
542    }
543
544    #[inline]
545    unsafe fn xlength_unchecked(&self) -> R_xlen_t {
546        unsafe { Rf_xlength_unchecked(*self) }
547    }
548
549    #[inline]
550    unsafe fn len_unchecked(&self) -> usize {
551        unsafe { Rf_xlength_unchecked(*self) as usize }
552    }
553
554    #[inline]
555    unsafe fn as_slice<T: RNativeType>(&self) -> &'static [T] {
556        debug_assert!(
557            self.type_of() == T::SEXP_TYPE,
558            "SEXP type mismatch: expected {:?}, got {:?}",
559            T::SEXP_TYPE,
560            self.type_of()
561        );
562        unsafe { crate::from_r::r_slice(DATAPTR_RO(*self).cast(), self.len()) }
563    }
564
565    #[inline]
566    unsafe fn as_mut_slice<T: RNativeType>(&self) -> &'static mut [T] {
567        debug_assert!(
568            self.type_of() == T::SEXP_TYPE,
569            "SEXP type mismatch: expected {:?}, got {:?}",
570            T::SEXP_TYPE,
571            self.type_of()
572        );
573        unsafe { crate::from_r::r_slice_mut(T::dataptr_mut(*self), self.len()) }
574    }
575
576    #[inline]
577    unsafe fn as_slice_unchecked<T: RNativeType>(&self) -> &'static [T] {
578        debug_assert!(
579            self.type_of() == T::SEXP_TYPE,
580            "SEXP type mismatch: expected {:?}, got {:?}",
581            T::SEXP_TYPE,
582            self.type_of()
583        );
584        unsafe { crate::from_r::r_slice(DATAPTR_RO_unchecked(*self).cast(), self.len_unchecked()) }
585    }
586
587    // Type checking methods
588
589    #[inline]
590    fn is_integer(&self) -> bool {
591        self.type_of() == SEXPTYPE::INTSXP
592    }
593
594    #[inline]
595    fn is_real(&self) -> bool {
596        self.type_of() == SEXPTYPE::REALSXP
597    }
598
599    #[inline]
600    fn is_logical(&self) -> bool {
601        self.type_of() == SEXPTYPE::LGLSXP
602    }
603
604    #[inline]
605    fn is_character(&self) -> bool {
606        self.type_of() == SEXPTYPE::STRSXP
607    }
608
609    #[inline]
610    fn is_raw(&self) -> bool {
611        self.type_of() == SEXPTYPE::RAWSXP
612    }
613
614    #[inline]
615    fn is_complex(&self) -> bool {
616        self.type_of() == SEXPTYPE::CPLXSXP
617    }
618
619    #[inline]
620    fn is_list(&self) -> bool {
621        self.type_of() == SEXPTYPE::VECSXP
622    }
623
624    #[inline]
625    fn is_external_ptr(&self) -> bool {
626        self.type_of() == SEXPTYPE::EXTPTRSXP
627    }
628
629    #[inline]
630    fn is_environment(&self) -> bool {
631        self.type_of() == SEXPTYPE::ENVSXP
632    }
633
634    #[inline]
635    fn is_symbol(&self) -> bool {
636        self.type_of() == SEXPTYPE::SYMSXP
637    }
638
639    #[inline]
640    fn is_language(&self) -> bool {
641        self.type_of() == SEXPTYPE::LANGSXP
642    }
643
644    #[inline]
645    fn is_altrep(&self) -> bool {
646        unsafe { ALTREP(*self) != 0 }
647    }
648
649    #[inline]
650    fn is_empty(&self) -> bool {
651        self.len() == 0
652    }
653
654    #[inline]
655    fn is_nil(&self) -> bool {
656        // Pointer comparison, not type dereference — safe on dangling pointers.
657        // R_NilValue is the singleton NILSXP; checking type_of() would crash
658        // on freed SEXPs during cleanup.
659        unsafe { std::ptr::addr_eq(self.0, R_NilValue.0) }
660    }
661
662    #[inline]
663    fn is_factor(&self) -> bool {
664        unsafe { Rf_isFactor(*self) != Rboolean::FALSE }
665    }
666
667    #[inline]
668    fn is_pair_list(&self) -> bool {
669        unsafe { Rf_isList(*self) != Rboolean::FALSE }
670    }
671
672    #[inline]
673    fn is_matrix(&self) -> bool {
674        unsafe { Rf_isMatrix(*self) != Rboolean::FALSE }
675    }
676
677    #[inline]
678    fn is_array(&self) -> bool {
679        unsafe { Rf_isArray(*self) != Rboolean::FALSE }
680    }
681
682    #[inline]
683    fn is_function(&self) -> bool {
684        unsafe { Rf_isFunction(*self) != Rboolean::FALSE }
685    }
686
687    #[inline]
688    fn is_s4(&self) -> bool {
689        unsafe { Rf_isS4(*self) != Rboolean::FALSE }
690    }
691
692    #[inline]
693    fn is_data_frame(&self) -> bool {
694        self.inherits_class(c"data.frame")
695    }
696
697    #[inline]
698    fn is_numeric(&self) -> bool {
699        let typ = self.type_of();
700        (typ == SEXPTYPE::INTSXP || typ == SEXPTYPE::LGLSXP || typ == SEXPTYPE::REALSXP)
701            && !self.is_factor()
702    }
703
704    #[inline]
705    fn is_number(&self) -> bool {
706        self.is_numeric() || self.is_complex()
707    }
708
709    #[inline]
710    fn is_vector_atomic(&self) -> bool {
711        matches!(
712            self.type_of(),
713            SEXPTYPE::LGLSXP
714                | SEXPTYPE::INTSXP
715                | SEXPTYPE::REALSXP
716                | SEXPTYPE::CPLXSXP
717                | SEXPTYPE::STRSXP
718                | SEXPTYPE::RAWSXP
719        )
720    }
721
722    #[inline]
723    fn is_vector_list(&self) -> bool {
724        let typ = self.type_of();
725        typ == SEXPTYPE::VECSXP || typ == SEXPTYPE::EXPRSXP
726    }
727
728    #[inline]
729    fn is_vector(&self) -> bool {
730        self.is_vector_atomic() || self.is_vector_list()
731    }
732
733    #[inline]
734    fn is_object(&self) -> bool {
735        unsafe { Rf_isObject(*self) != Rboolean::FALSE }
736    }
737
738    // region: Coercion and scalar extraction
739
740    #[inline]
741    fn coerce(&self, target: SEXPTYPE) -> SEXP {
742        unsafe { Rf_coerceVector(*self, target) }
743    }
744
745    #[inline]
746    fn as_logical(&self) -> Option<bool> {
747        let v = unsafe { Rf_asLogical(*self) };
748        if v == crate::altrep_traits::NA_LOGICAL {
749            None
750        } else {
751            Some(v != 0)
752        }
753    }
754
755    #[inline]
756    fn as_integer(&self) -> Option<i32> {
757        let v = unsafe { Rf_asInteger(*self) };
758        if v == crate::altrep_traits::NA_INTEGER {
759            None
760        } else {
761            Some(v)
762        }
763    }
764
765    #[inline]
766    fn as_real(&self) -> Option<f64> {
767        let v = unsafe { Rf_asReal(*self) };
768        if v.to_bits() == crate::altrep_traits::NA_REAL.to_bits() {
769            None
770        } else {
771            Some(v)
772        }
773    }
774
775    #[inline]
776    fn as_char(&self) -> SEXP {
777        unsafe { Rf_asChar(*self) }
778    }
779
780    // endregion
781
782    // region: Attribute access
783
784    #[inline]
785    fn get_attr(&self, name: SEXP) -> SEXP {
786        unsafe { Rf_getAttrib(*self, name) }
787    }
788
789    #[inline]
790    fn set_attr(&self, name: SEXP, val: SEXP) {
791        unsafe {
792            Rf_setAttrib(*self, name, val);
793        }
794    }
795
796    #[inline]
797    fn get_names(&self) -> SEXP {
798        unsafe { Rf_getAttrib(*self, R_NamesSymbol) }
799    }
800
801    #[inline]
802    fn set_names(&self, names: SEXP) {
803        unsafe {
804            Rf_namesgets(*self, names);
805        }
806    }
807
808    #[inline]
809    fn get_class(&self) -> SEXP {
810        unsafe { Rf_getAttrib(*self, R_ClassSymbol) }
811    }
812
813    #[inline]
814    fn set_class(&self, class: SEXP) {
815        unsafe {
816            Rf_classgets(*self, class);
817        }
818    }
819
820    #[inline]
821    fn get_dim(&self) -> SEXP {
822        unsafe { Rf_getAttrib(*self, R_DimSymbol) }
823    }
824
825    #[inline]
826    fn set_dim(&self, dim: SEXP) {
827        unsafe {
828            Rf_dimgets(*self, dim);
829        }
830    }
831
832    #[inline]
833    fn get_dimnames(&self) -> SEXP {
834        unsafe { Rf_getAttrib(*self, R_DimNamesSymbol) }
835    }
836
837    #[inline]
838    fn set_dimnames(&self, dimnames: SEXP) {
839        unsafe {
840            Rf_dimnamesgets(*self, dimnames);
841        }
842    }
843
844    #[inline]
845    fn get_levels(&self) -> SEXP {
846        unsafe { Rf_getAttrib(*self, R_LevelsSymbol) }
847    }
848
849    #[inline]
850    fn set_levels(&self, levels: SEXP) {
851        unsafe {
852            Rf_setAttrib(*self, R_LevelsSymbol, levels);
853        }
854    }
855
856    #[inline]
857    fn get_row_names(&self) -> SEXP {
858        unsafe { Rf_getAttrib(*self, R_RowNamesSymbol) }
859    }
860
861    #[inline]
862    fn set_row_names(&self, row_names: SEXP) {
863        unsafe {
864            Rf_setAttrib(*self, R_RowNamesSymbol, row_names);
865        }
866    }
867
868    #[inline]
869    fn inherits_class(&self, class: &std::ffi::CStr) -> bool {
870        unsafe { Rf_inherits(*self, class.as_ptr()) != Rboolean::FALSE }
871    }
872
873    // endregion
874
875    // region: String element access
876
877    #[inline]
878    fn string_elt(&self, i: isize) -> SEXP {
879        unsafe { STRING_ELT(*self, i) }
880    }
881
882    #[inline]
883    fn string_elt_str(&self, i: isize) -> Option<&str> {
884        unsafe {
885            let charsxp = STRING_ELT(*self, i);
886            if std::ptr::addr_eq(charsxp.0, R_NaString.0) {
887                return None;
888            }
889            let p = R_CHAR(charsxp);
890            Some(std::ffi::CStr::from_ptr(p).to_str().unwrap_or(""))
891        }
892    }
893
894    #[inline]
895    fn set_string_elt(&self, i: isize, charsxp: SEXP) {
896        unsafe { SET_STRING_ELT(*self, i, charsxp) }
897    }
898
899    #[inline]
900    fn is_na_string(&self) -> bool {
901        unsafe { std::ptr::addr_eq(self.0, R_NaString.0) }
902    }
903
904    // endregion
905
906    // region: List element access
907
908    #[inline]
909    fn vector_elt(&self, i: isize) -> SEXP {
910        unsafe { VECTOR_ELT(*self, i) }
911    }
912
913    #[inline]
914    fn set_vector_elt(&self, i: isize, val: SEXP) {
915        unsafe {
916            SET_VECTOR_ELT(*self, i, val);
917        }
918    }
919
920    // endregion
921
922    // region: Typed single-element access
923
924    #[inline]
925    fn integer_elt(&self, i: isize) -> i32 {
926        unsafe { INTEGER_ELT(*self, i) }
927    }
928    #[inline]
929    fn real_elt(&self, i: isize) -> f64 {
930        unsafe { REAL_ELT(*self, i) }
931    }
932    #[inline]
933    fn logical_elt(&self, i: isize) -> i32 {
934        unsafe { LOGICAL_ELT(*self, i) }
935    }
936    #[inline]
937    fn complex_elt(&self, i: isize) -> Rcomplex {
938        unsafe { COMPLEX_ELT(*self, i) }
939    }
940    #[inline]
941    fn raw_elt(&self, i: isize) -> u8 {
942        unsafe { RAW_ELT(*self, i) }
943    }
944    #[inline]
945    fn set_integer_elt(&self, i: isize, v: i32) {
946        unsafe { SET_INTEGER_ELT(*self, i, v) }
947    }
948    #[inline]
949    fn set_real_elt(&self, i: isize, v: f64) {
950        unsafe { SET_REAL_ELT(*self, i, v) }
951    }
952    #[inline]
953    fn set_logical_elt(&self, i: isize, v: i32) {
954        unsafe { SET_LOGICAL_ELT(*self, i, v) }
955    }
956    #[inline]
957    fn set_complex_elt(&self, i: isize, v: Rcomplex) {
958        unsafe { SET_COMPLEX_ELT(*self, i, v) }
959    }
960    #[inline]
961    fn set_raw_elt(&self, i: isize, v: u8) {
962        unsafe { SET_RAW_ELT(*self, i, v) }
963    }
964
965    // endregion
966
967    // region: Symbol and CHARSXP access
968
969    #[inline]
970    fn printname(&self) -> SEXP {
971        unsafe { PRINTNAME(*self) }
972    }
973
974    #[inline]
975    fn r_char(&self) -> *const ::std::os::raw::c_char {
976        unsafe { R_CHAR(*self) }
977    }
978
979    #[inline]
980    fn r_char_str(&self) -> Option<&str> {
981        if self.is_na_string() {
982            return None;
983        }
984        let p = unsafe { R_CHAR(*self) };
985        Some(
986            unsafe { std::ffi::CStr::from_ptr(p) }
987                .to_str()
988                .unwrap_or(""),
989        )
990    }
991
992    // endregion
993
994    // region: Vector resizing
995
996    #[inline]
997    fn resize(&self, newlen: R_xlen_t) -> SEXP {
998        unsafe { Rf_xlengthgets(*self, newlen) }
999    }
1000
1001    // endregion
1002
1003    // region: Duplication
1004
1005    #[inline]
1006    fn duplicate(&self) -> SEXP {
1007        unsafe { Rf_duplicate(*self) }
1008    }
1009
1010    #[inline]
1011    fn shallow_duplicate(&self) -> SEXP {
1012        unsafe { Rf_shallow_duplicate(*self) }
1013    }
1014
1015    // endregion
1016
1017    // region: Unchecked variants
1018
1019    #[inline]
1020    unsafe fn string_elt_unchecked(&self, i: isize) -> SEXP {
1021        unsafe { STRING_ELT_unchecked(*self, i) }
1022    }
1023
1024    #[inline]
1025    unsafe fn set_string_elt_unchecked(&self, i: isize, charsxp: SEXP) {
1026        unsafe { SET_STRING_ELT_unchecked(*self, i, charsxp) }
1027    }
1028
1029    #[inline]
1030    unsafe fn vector_elt_unchecked(&self, i: isize) -> SEXP {
1031        unsafe { VECTOR_ELT_unchecked(*self, i) }
1032    }
1033
1034    #[inline]
1035    unsafe fn set_vector_elt_unchecked(&self, i: isize, val: SEXP) {
1036        unsafe {
1037            SET_VECTOR_ELT_unchecked(*self, i, val);
1038        }
1039    }
1040
1041    #[inline]
1042    unsafe fn get_attr_unchecked(&self, name: SEXP) -> SEXP {
1043        unsafe { Rf_getAttrib_unchecked(*self, name) }
1044    }
1045
1046    #[inline]
1047    unsafe fn set_attr_unchecked(&self, name: SEXP, val: SEXP) {
1048        unsafe {
1049            Rf_setAttrib_unchecked(*self, name, val);
1050        }
1051    }
1052
1053    #[inline]
1054    unsafe fn r_char_unchecked(&self) -> *const ::std::os::raw::c_char {
1055        unsafe { R_CHAR_unchecked(*self) }
1056    }
1057
1058    // endregion
1059}
1060
1061/// Extension trait for SEXP providing pairlist (cons cell) operations.
1062///
1063/// Pairlist nodes have three slots: CAR (value), CDR (next), and TAG (name).
1064/// This trait encapsulates the raw C functions behind method calls.
1065#[allow(dead_code)]
1066pub(crate) trait PairListExt {
1067    /// Create a cons cell with this SEXP as CAR and `cdr` as CDR.
1068    fn cons(self, cdr: SEXP) -> SEXP;
1069
1070    /// Create a language cons cell with this SEXP as CAR and `cdr` as CDR.
1071    fn lcons(self, cdr: SEXP) -> SEXP;
1072
1073    /// Get the CAR (head/value) of this pairlist node.
1074    fn car(&self) -> SEXP;
1075
1076    /// Get the CDR (tail/rest) of this pairlist node.
1077    fn cdr(&self) -> SEXP;
1078
1079    /// Get the TAG (name symbol) of this pairlist node.
1080    fn tag(&self) -> SEXP;
1081
1082    /// Set the TAG (name symbol) of this pairlist node.
1083    fn set_tag(&self, tag: SEXP);
1084
1085    /// Set the CAR (value) of this pairlist node.
1086    fn set_car(&self, value: SEXP) -> SEXP;
1087
1088    /// Set the CDR (tail) of this pairlist node.
1089    fn set_cdr(&self, tail: SEXP) -> SEXP;
1090
1091    /// Create a cons cell (no thread check).
1092    /// # Safety
1093    /// Must be called from R's main thread.
1094    unsafe fn cons_unchecked(self, cdr: SEXP) -> SEXP;
1095
1096    /// Get the CAR (no thread check).
1097    /// # Safety
1098    /// Must be called from R's main thread.
1099    unsafe fn car_unchecked(&self) -> SEXP;
1100
1101    /// Get the CDR (no thread check).
1102    /// # Safety
1103    /// Must be called from R's main thread.
1104    unsafe fn cdr_unchecked(&self) -> SEXP;
1105
1106    /// Set the TAG (no thread check).
1107    /// # Safety
1108    /// Must be called from R's main thread.
1109    unsafe fn set_tag_unchecked(&self, tag: SEXP);
1110
1111    /// Set the CAR (no thread check).
1112    /// # Safety
1113    /// Must be called from R's main thread.
1114    unsafe fn set_car_unchecked(&self, value: SEXP) -> SEXP;
1115
1116    /// Set the CDR (no thread check).
1117    /// # Safety
1118    /// Must be called from R's main thread.
1119    unsafe fn set_cdr_unchecked(&self, tail: SEXP) -> SEXP;
1120}
1121
1122impl PairListExt for SEXP {
1123    #[inline]
1124    fn cons(self, cdr: SEXP) -> SEXP {
1125        unsafe { Rf_cons(self, cdr) }
1126    }
1127
1128    #[inline]
1129    fn lcons(self, cdr: SEXP) -> SEXP {
1130        unsafe { Rf_lcons(self, cdr) }
1131    }
1132
1133    #[inline]
1134    fn car(&self) -> SEXP {
1135        unsafe { CAR(*self) }
1136    }
1137
1138    #[inline]
1139    fn cdr(&self) -> SEXP {
1140        unsafe { CDR(*self) }
1141    }
1142
1143    #[inline]
1144    fn tag(&self) -> SEXP {
1145        unsafe { TAG(*self) }
1146    }
1147
1148    #[inline]
1149    fn set_tag(&self, tag: SEXP) {
1150        unsafe { SET_TAG(*self, tag) }
1151    }
1152
1153    #[inline]
1154    fn set_car(&self, value: SEXP) -> SEXP {
1155        unsafe { SETCAR(*self, value) }
1156    }
1157
1158    #[inline]
1159    fn set_cdr(&self, tail: SEXP) -> SEXP {
1160        unsafe { SETCDR(*self, tail) }
1161    }
1162
1163    #[inline]
1164    unsafe fn cons_unchecked(self, cdr: SEXP) -> SEXP {
1165        unsafe { Rf_cons_unchecked(self, cdr) }
1166    }
1167
1168    #[inline]
1169    unsafe fn car_unchecked(&self) -> SEXP {
1170        unsafe { CAR_unchecked(*self) }
1171    }
1172
1173    #[inline]
1174    unsafe fn cdr_unchecked(&self) -> SEXP {
1175        unsafe { CDR_unchecked(*self) }
1176    }
1177
1178    #[inline]
1179    unsafe fn set_tag_unchecked(&self, tag: SEXP) {
1180        unsafe { SET_TAG_unchecked(*self, tag) }
1181    }
1182
1183    #[inline]
1184    unsafe fn set_car_unchecked(&self, value: SEXP) -> SEXP {
1185        unsafe { SETCAR_unchecked(*self, value) }
1186    }
1187
1188    #[inline]
1189    unsafe fn set_cdr_unchecked(&self, tail: SEXP) -> SEXP {
1190        unsafe { SETCDR_unchecked(*self, tail) }
1191    }
1192}