Skip to main content

miniextendr_api/
ffi.rs

1//! Low-level FFI bindings to R headers.
2//!
3//! This module mirrors R's C API closely and is intentionally thin. Most
4//! downstream code should prefer higher-level wrappers in the crate root.
5
6/// ALTREP-specific C API bindings.
7pub mod altrep;
8
9#[allow(non_camel_case_types)]
10/// R's extended vector length type (`R_xlen_t`).
11pub type R_xlen_t = isize;
12/// R byte element type used by `RAWSXP`.
13pub type Rbyte = ::std::os::raw::c_uchar;
14
15/// R's complex scalar layout (`Rcomplex`).
16#[repr(C)]
17#[derive(Debug, Copy, Clone, PartialEq)]
18pub struct Rcomplex {
19    /// Real part.
20    pub r: f64,
21    /// Imaginary part.
22    pub i: f64,
23}
24
25/// R S-expression tag values (`SEXPTYPE`).
26#[repr(u32)]
27#[non_exhaustive]
28#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
29pub enum SEXPTYPE {
30    #[doc = " nil = NULL"]
31    NILSXP = 0,
32    #[doc = " symbols"]
33    SYMSXP = 1,
34    #[doc = " lists of dotted pairs"]
35    LISTSXP = 2,
36    #[doc = " closures"]
37    CLOSXP = 3,
38    #[doc = " environments"]
39    ENVSXP = 4,
40    #[doc = r" promises: \[un\]evaluated closure arguments"]
41    PROMSXP = 5,
42    #[doc = " language constructs (special lists)"]
43    LANGSXP = 6,
44    #[doc = " special forms"]
45    SPECIALSXP = 7,
46    #[doc = " builtin non-special forms"]
47    BUILTINSXP = 8,
48    #[doc = " \"scalar\" string type (internal only)"]
49    CHARSXP = 9,
50    #[doc = " logical vectors"]
51    LGLSXP = 10,
52    #[doc = " integer vectors"]
53    INTSXP = 13,
54    #[doc = " real variables"]
55    REALSXP = 14,
56    #[doc = " complex variables"]
57    CPLXSXP = 15,
58    #[doc = " string vectors"]
59    STRSXP = 16,
60    #[doc = " dot-dot-dot object"]
61    DOTSXP = 17,
62    #[doc = " make \"any\" args work"]
63    ANYSXP = 18,
64    #[doc = " generic vectors"]
65    VECSXP = 19,
66    #[doc = " expressions vectors"]
67    EXPRSXP = 20,
68    #[doc = " byte code"]
69    BCODESXP = 21,
70    #[doc = " external pointer"]
71    EXTPTRSXP = 22,
72    #[doc = " weak reference"]
73    WEAKREFSXP = 23,
74    #[doc = " raw bytes"]
75    RAWSXP = 24,
76    #[doc = " S4 non-vector"]
77    S4SXP = 25,
78    #[doc = " fresh node created in new page"]
79    NEWSXP = 30,
80    #[doc = " node released by GC"]
81    FREESXP = 31,
82    #[doc = " Closure or Builtin"]
83    FUNSXP = 99,
84}
85
86impl SEXPTYPE {
87    /// Alias for `S4SXP` (value 25).
88    ///
89    /// R defines both `OBJSXP` and `S4SXP` as value 25. `S4SXP` is retained
90    /// for backwards compatibility; `OBJSXP` is the preferred name.
91    pub const OBJSXP: SEXPTYPE = SEXPTYPE::S4SXP;
92
93    /// Get R's name for this SEXPTYPE (e.g. `"double"`, `"integer"`, `"list"`).
94    ///
95    /// Returns the same string as R's `typeof()` function.
96    #[inline]
97    pub fn type_name(self) -> &'static str {
98        let cstr = unsafe { Rf_type2char(self) };
99        // SAFETY: R's type names are static ASCII strings
100        unsafe { std::ffi::CStr::from_ptr(cstr) }
101            .to_str()
102            .unwrap_or("unknown")
103    }
104}
105
106#[repr(transparent)]
107#[derive(Debug)]
108/// Opaque underlying S-expression header type.
109pub struct SEXPREC(::std::os::raw::c_void);
110
111/// R's pointer type for S-expressions.
112///
113/// This is a newtype wrapper around `*mut SEXPREC` that implements Send and Sync.
114/// SEXP is just a handle (pointer) - the actual data it points to is managed by R's
115/// garbage collector and should only be accessed on R's main thread.
116///
117/// # Safety
118///
119/// While SEXP is Send+Sync (allowing it to be passed between threads), the data
120/// it points to must only be accessed on R's main thread. The miniextendr runtime
121/// enforces this through the worker thread pattern.
122///
123/// # Equality Semantics
124///
125/// IMPORTANT: The derived `PartialEq` compares **pointer equality**, not semantic equality.
126/// For proper R semantics (comparing object contents), use [`R_compute_identical`].
127///
128/// ```ignore
129/// // Pointer equality (fast, often wrong for R semantics)
130/// if sexp1 == sexp2 { ... }  // Only true if same pointer
131///
132/// // Semantic equality (correct R semantics)
133/// if R_compute_identical(sexp1, sexp2, 16) != 0 { ... }
134/// ```
135///
136/// **Hash trait removed**: SEXP no longer implements `Hash` because proper hashing
137/// would require deep content inspection via `R_compute_identical`, which is too
138/// expensive for general use. If you need SEXP as a HashMap key, use pointer identity:
139///
140/// ```ignore
141/// // Store by pointer identity (common pattern for R symbol lookups)
142/// let mut map: HashMap<*mut SEXPREC, Value> = HashMap::new();
143/// map.insert(sexp.as_ptr(), value);
144/// ```
145#[repr(transparent)]
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub struct SEXP(pub *mut SEXPREC);
148
149// SAFETY: SEXP is just a pointer (memory address). Passing the address between
150// threads is safe. The actual data access is protected by miniextendr's runtime
151// which ensures R API calls happen on the main thread.
152unsafe impl Send for SEXP {}
153unsafe impl Sync for SEXP {}
154
155impl SEXP {
156    /// Create a C null pointer SEXP (0x0).
157    ///
158    /// This is **not** R's `NULL` value (`R_NilValue`). R's `NULL` is a real
159    /// heap-allocated singleton; a C null pointer is just address zero. Passing
160    /// `SEXP::null()` where R expects `R_NilValue` will corrupt R's GC state
161    /// and likely segfault.
162    ///
163    /// Use [`SEXP::nil()`] for R's `NULL`. Only use `null()` for low-level
164    /// pointer initialization, ALTREP Sum/Min/Max "can't compute" returns
165    /// (R checks `!= NULL`, not `!= R_NilValue`), or comparison against
166    /// uninitialized pointers.
167    ///
168    /// See also: [`SEXP::nil()`], [`SEXP::is_null()`], [`SexpExt::is_nil()`]
169    #[inline]
170    pub const fn null() -> Self {
171        Self(std::ptr::null_mut())
172    }
173
174    /// Return R's `NULL` singleton (`R_NilValue`).
175    ///
176    /// This is **not** a C null pointer — it points to R's actual nil object
177    /// on the heap. Use this for `.Call()` return values, SEXP arguments to
178    /// R API functions, and any slot in R data structures.
179    ///
180    /// See also: [`SEXP::null()`], [`SexpExt::is_nil()`], [`SEXP::is_null()`]
181    #[inline]
182    pub fn nil() -> Self {
183        unsafe { R_NilValue }
184    }
185
186    /// Check if this SEXP is a C null pointer (0x0).
187    ///
188    /// To check if an SEXP is R's `NULL` (`R_NilValue`), use
189    /// [`SexpExt::is_nil()`] instead.
190    ///
191    /// See also: [`SexpExt::is_nil()`], [`SexpExt::is_null_or_nil()`]
192    #[inline]
193    pub const fn is_null(self) -> bool {
194        self.0.is_null()
195    }
196
197    /// Get the raw pointer.
198    #[inline]
199    pub const fn as_ptr(self) -> *mut SEXPREC {
200        self.0
201    }
202
203    /// Create from a raw pointer.
204    #[inline]
205    pub const fn from_ptr(ptr: *mut SEXPREC) -> Self {
206        Self(ptr)
207    }
208
209    // region: String construction
210
211    /// Create a CHARSXP from a Rust `&str` (UTF-8).
212    #[inline]
213    pub fn charsxp(s: &str) -> SEXP {
214        let len: i32 = s.len().try_into().expect("string exceeds i32::MAX bytes");
215        unsafe { Rf_mkCharLenCE(s.as_ptr().cast(), len, CE_UTF8) }
216    }
217
218    /// R's `NA_character_` singleton.
219    #[inline]
220    pub fn na_string() -> SEXP {
221        unsafe { R_NaString }
222    }
223
224    /// R's empty string `""` singleton.
225    #[inline]
226    pub fn blank_string() -> SEXP {
227        unsafe { R_BlankString }
228    }
229
230    /// Create an R symbol (SYMSXP) from a CHARSXP.
231    ///
232    /// Equivalent to `Rf_installChar(charsxp)`. The symbol is interned
233    /// in R's global symbol table and never garbage collected.
234    #[inline]
235    pub fn install_char(charsxp: SEXP) -> SEXP {
236        unsafe { Rf_installChar(charsxp) }
237    }
238
239    /// Create an R symbol (SYMSXP) from a Rust `&str`.
240    ///
241    /// Combines `SEXP::charsxp()` + `Rf_installChar` into one call.
242    /// The symbol is interned and never garbage collected.
243    #[inline]
244    pub fn symbol(name: &str) -> SEXP {
245        Self::install_char(Self::charsxp(name))
246    }
247
248    // endregion
249
250    // region: Scalar construction
251
252    /// Create a length-1 integer vector.
253    #[inline]
254    pub fn scalar_integer(x: i32) -> SEXP {
255        unsafe { Rf_ScalarInteger(x) }
256    }
257
258    /// Create a length-1 real vector.
259    #[inline]
260    pub fn scalar_real(x: f64) -> SEXP {
261        unsafe { Rf_ScalarReal(x) }
262    }
263
264    /// Create a length-1 logical vector.
265    #[inline]
266    pub fn scalar_logical(x: bool) -> SEXP {
267        unsafe { Rf_ScalarLogical(if x { 1 } else { 0 }) }
268    }
269
270    /// Create a length-1 logical vector from raw i32 (0=FALSE, 1=TRUE, NA_LOGICAL=NA).
271    #[inline]
272    /// Accepts 0 (FALSE), 1 (TRUE), or `NA_LOGICAL` (`i32::MIN`) for NA.
273    /// Prefer [`scalar_logical`](Self::scalar_logical) for non-NA values.
274    pub fn scalar_logical_raw(x: i32) -> SEXP {
275        unsafe { Rf_ScalarLogical(x) }
276    }
277
278    /// Create a length-1 raw vector.
279    #[inline]
280    pub fn scalar_raw(x: u8) -> SEXP {
281        unsafe { Rf_ScalarRaw(x) }
282    }
283
284    /// Create a length-1 complex vector.
285    #[inline]
286    pub fn scalar_complex(x: Rcomplex) -> SEXP {
287        unsafe { Rf_ScalarComplex(x) }
288    }
289
290    /// Create a length-1 character vector from a CHARSXP.
291    #[inline]
292    pub fn scalar_string(charsxp: SEXP) -> SEXP {
293        unsafe { Rf_ScalarString(charsxp) }
294    }
295
296    /// Create a length-1 character vector from a Rust `&str`.
297    #[inline]
298    pub fn scalar_string_from_str(s: &str) -> SEXP {
299        Self::scalar_string(Self::charsxp(s))
300    }
301
302    // endregion
303
304    // region: R global symbols and singletons
305
306    /// R's `names` attribute symbol.
307    #[inline]
308    pub fn names_symbol() -> SEXP {
309        unsafe { R_NamesSymbol }
310    }
311
312    /// R's `dim` attribute symbol.
313    #[inline]
314    pub fn dim_symbol() -> SEXP {
315        unsafe { R_DimSymbol }
316    }
317
318    /// R's `dimnames` attribute symbol.
319    #[inline]
320    pub fn dimnames_symbol() -> SEXP {
321        unsafe { R_DimNamesSymbol }
322    }
323
324    /// R's `class` attribute symbol.
325    #[inline]
326    pub fn class_symbol() -> SEXP {
327        unsafe { R_ClassSymbol }
328    }
329
330    /// R's `levels` attribute symbol (factors).
331    #[inline]
332    pub fn levels_symbol() -> SEXP {
333        unsafe { R_LevelsSymbol }
334    }
335
336    /// R's `tsp` attribute symbol (time series).
337    #[inline]
338    pub fn tsp_symbol() -> SEXP {
339        unsafe { R_TspSymbol }
340    }
341
342    /// R's base namespace environment.
343    #[inline]
344    pub fn base_namespace() -> SEXP {
345        unsafe { R_BaseNamespace }
346    }
347
348    /// R's missing argument sentinel.
349    #[inline]
350    pub fn missing_arg() -> SEXP {
351        unsafe { R_MissingArg }
352    }
353
354    // endregion
355}
356
357impl Default for SEXP {
358    #[inline]
359    fn default() -> Self {
360        Self::null()
361    }
362}
363
364impl From<*mut SEXPREC> for SEXP {
365    #[inline]
366    fn from(ptr: *mut SEXPREC) -> Self {
367        Self(ptr)
368    }
369}
370
371impl From<SEXP> for *mut SEXPREC {
372    #[inline]
373    fn from(sexp: SEXP) -> Self {
374        sexp.0
375    }
376}
377
378/// Extension trait for SEXP providing safe(r) accessors and type checking.
379///
380/// This trait provides idiomatic Rust methods for working with SEXPs,
381/// equivalent to R's inline macros and type checking functions.
382pub trait SexpExt {
383    /// Get the type of this SEXP.
384    ///
385    /// Equivalent to `TYPEOF(x)` macro.
386    ///
387    /// # Safety
388    ///
389    /// The SEXP must be valid (not null and not freed).
390    fn type_of(&self) -> SEXPTYPE;
391
392    /// Check if this SEXP is null or R_NilValue.
393    fn is_null_or_nil(&self) -> bool;
394
395    /// Get the length of this SEXP as `usize`.
396    ///
397    /// # Safety
398    ///
399    /// The SEXP must be valid.
400    fn len(&self) -> usize;
401
402    /// Get the length as `R_xlen_t`.
403    ///
404    /// # Safety
405    ///
406    /// The SEXP must be valid.
407    fn xlength(&self) -> R_xlen_t;
408
409    /// Get the length without thread checks.
410    ///
411    /// # Safety
412    ///
413    /// Must be called from R's main thread. No debug assertions.
414    unsafe fn len_unchecked(&self) -> usize;
415
416    /// Get a slice view of this SEXP's data.
417    ///
418    /// # Safety
419    ///
420    /// - The SEXP must be valid and of the correct type for `T`
421    /// - The SEXP must be protected from R's garbage collector for the entire
422    ///   duration the returned slice is used. This typically means the SEXP must
423    ///   be either:
424    ///   - An argument to a `.Call` function (protected by R's calling convention)
425    ///   - Explicitly protected via `PROTECT`/`UNPROTECT` or `R_PreserveObject`
426    ///   - Part of a protected container (e.g., element of a protected list)
427    /// - The returned slice has `'static` lifetime for API convenience, but this
428    ///   is a lie - the actual lifetime is tied to the SEXP's protection status.
429    ///   Holding the slice after the SEXP is unprotected is undefined behavior.
430    unsafe fn as_slice<T: RNativeType>(&self) -> &'static [T];
431
432    /// Get a slice view without thread checks.
433    ///
434    /// # Safety
435    ///
436    /// - All safety requirements of [`as_slice`](Self::as_slice) apply
437    /// - Additionally, must be called from R's main thread (no debug assertions)
438    unsafe fn as_slice_unchecked<T: RNativeType>(&self) -> &'static [T];
439
440    /// Get a mutable slice view of this SEXP's data.
441    ///
442    /// # Safety
443    ///
444    /// - All safety requirements of [`as_slice`](Self::as_slice) apply.
445    /// - The caller must ensure **exclusive access**: no other `&[T]` or `&mut [T]`
446    ///   slices derived from this SEXP may exist simultaneously. Multiple calls to
447    ///   `as_mut_slice` on the same SEXP without dropping the previous slice is UB.
448    /// - The SEXP must not be shared (ALTREP or NAMED > 0 objects may alias).
449    unsafe fn as_mut_slice<T: RNativeType>(&self) -> &'static mut [T];
450
451    // Type checking methods (equivalent to R's type check macros)
452
453    /// Check if this SEXP is an integer vector (INTSXP).
454    fn is_integer(&self) -> bool;
455
456    /// Check if this SEXP is a real/numeric vector (REALSXP).
457    fn is_real(&self) -> bool;
458
459    /// Check if this SEXP is a logical vector (LGLSXP).
460    fn is_logical(&self) -> bool;
461
462    /// Check if this SEXP is a character/string vector (STRSXP).
463    fn is_character(&self) -> bool;
464
465    /// Check if this SEXP is a raw vector (RAWSXP).
466    fn is_raw(&self) -> bool;
467
468    /// Check if this SEXP is a complex vector (CPLXSXP).
469    fn is_complex(&self) -> bool;
470
471    /// Check if this SEXP is a list/generic vector (VECSXP).
472    fn is_list(&self) -> bool;
473
474    /// Check if this SEXP is an external pointer (EXTPTRSXP).
475    fn is_external_ptr(&self) -> bool;
476
477    /// Check if this SEXP is an environment (ENVSXP).
478    fn is_environment(&self) -> bool;
479
480    /// Check if this SEXP is a symbol (SYMSXP).
481    fn is_symbol(&self) -> bool;
482
483    /// Check if this SEXP is a language object (LANGSXP).
484    fn is_language(&self) -> bool;
485
486    /// Check if this SEXP is an ALTREP object.
487    ///
488    /// Equivalent to R's `ALTREP(x)` macro.
489    fn is_altrep(&self) -> bool;
490
491    /// Check if this `SEXP` contains any elements.
492    fn is_empty(&self) -> bool;
493
494    /// Check if this SEXP is R's `NULL` (NILSXP).
495    fn is_nil(&self) -> bool;
496
497    /// Check if this SEXP is a factor.
498    ///
499    /// Equivalent to R's `Rf_isFactor(x)`.
500    fn is_factor(&self) -> bool;
501
502    /// Check if this SEXP is a pairlist (LISTSXP or NILSXP).
503    ///
504    /// Equivalent to R's `Rf_isList(x)`.
505    fn is_pair_list(&self) -> bool;
506
507    /// Check if this SEXP is a matrix.
508    ///
509    /// Equivalent to R's `Rf_isMatrix(x)`.
510    fn is_matrix(&self) -> bool;
511
512    /// Check if this SEXP is an array.
513    ///
514    /// Equivalent to R's `Rf_isArray(x)`.
515    fn is_array(&self) -> bool;
516
517    /// Check if this SEXP is a function (closure, builtin, or special).
518    ///
519    /// Equivalent to R's `Rf_isFunction(x)`.
520    fn is_function(&self) -> bool;
521
522    /// Check if this SEXP is an S4 object.
523    ///
524    /// Equivalent to R's `Rf_isS4(x)`.
525    fn is_s4(&self) -> bool;
526
527    /// Check if this SEXP is a data.frame.
528    ///
529    /// Equivalent to R's `Rf_isDataFrame(x)`.
530    fn is_data_frame(&self) -> bool;
531
532    /// Check if this SEXP is a numeric type (integer, logical, or real, excluding factors).
533    ///
534    /// Equivalent to R's `Rf_isNumeric(x)`.
535    fn is_numeric(&self) -> bool;
536
537    /// Check if this SEXP is a number type (numeric or complex).
538    ///
539    /// Equivalent to R's `Rf_isNumber(x)`.
540    fn is_number(&self) -> bool;
541
542    /// Check if this SEXP is an atomic vector.
543    ///
544    /// Returns true for logical, integer, real, complex, character, and raw vectors.
545    fn is_vector_atomic(&self) -> bool;
546
547    /// Check if this SEXP is a vector list (VECSXP or EXPRSXP).
548    fn is_vector_list(&self) -> bool;
549
550    /// Check if this SEXP is a vector (atomic vector or list).
551    fn is_vector(&self) -> bool;
552
553    /// Check if this SEXP is an R "object" (has a class attribute).
554    fn is_object(&self) -> bool;
555
556    // region: Coercion and scalar extraction
557
558    /// Coerce this SEXP to the given type, returning a new SEXP.
559    ///
560    /// The result is guaranteed to have the requested SEXPTYPE.
561    /// Equivalent to R's `Rf_coerceVector(x, target)`.
562    fn coerce(&self, target: SEXPTYPE) -> SEXP;
563
564    /// Extract a scalar logical value.
565    ///
566    /// Returns `None` for `NA`. Coerces non-logical inputs.
567    /// Equivalent to R's `Rf_asLogical(x)`.
568    fn as_logical(&self) -> Option<bool>;
569
570    /// Extract a scalar integer value.
571    ///
572    /// Returns `None` for `NA_integer_`. Coerces non-integer inputs.
573    /// Equivalent to R's `Rf_asInteger(x)`.
574    fn as_integer(&self) -> Option<i32>;
575
576    /// Extract a scalar real value.
577    ///
578    /// Returns `None` for `NA_real_` (NaN). Coerces non-real inputs.
579    /// Equivalent to R's `Rf_asReal(x)`.
580    fn as_real(&self) -> Option<f64>;
581
582    /// Extract a scalar CHARSXP from this SEXP.
583    ///
584    /// The result is guaranteed to be a CHARSXP.
585    /// Equivalent to R's `Rf_asChar(x)`.
586    fn as_char(&self) -> SEXP;
587
588    // endregion
589
590    // region: Attribute access
591
592    /// Get an attribute by symbol.
593    fn get_attr(&self, name: SEXP) -> SEXP;
594
595    /// Get an attribute by symbol, returning `None` for `R_NilValue`.
596    fn get_attr_opt(&self, name: SEXP) -> Option<SEXP> {
597        let attr = self.get_attr(name);
598        if attr.is_nil() { None } else { Some(attr) }
599    }
600
601    /// Set an attribute by symbol.
602    fn set_attr(&self, name: SEXP, val: SEXP);
603
604    /// Get the `names` attribute.
605    fn get_names(&self) -> SEXP;
606
607    /// Set the `names` attribute.
608    fn set_names(&self, names: SEXP);
609
610    /// Get the `class` attribute.
611    fn get_class(&self) -> SEXP;
612
613    /// Set the `class` attribute.
614    fn set_class(&self, class: SEXP);
615
616    /// Get the `dim` attribute.
617    fn get_dim(&self) -> SEXP;
618
619    /// Set the `dim` attribute.
620    fn set_dim(&self, dim: SEXP);
621
622    /// Get the `dimnames` attribute.
623    fn get_dimnames(&self) -> SEXP;
624
625    /// Set the `dimnames` attribute.
626    fn set_dimnames(&self, dimnames: SEXP);
627
628    /// Get the `levels` attribute (factors).
629    fn get_levels(&self) -> SEXP;
630
631    /// Set the `levels` attribute (factors).
632    fn set_levels(&self, levels: SEXP);
633
634    /// Get the `row.names` attribute.
635    fn get_row_names(&self) -> SEXP;
636
637    /// Set the `row.names` attribute.
638    fn set_row_names(&self, row_names: SEXP);
639
640    /// Check if this SEXP inherits from a class.
641    ///
642    /// Equivalent to R's `inherits(x, "class_name")`.
643    fn inherits_class(&self, class: &std::ffi::CStr) -> bool;
644
645    // endregion
646
647    // region: String element access
648
649    /// Get the i-th CHARSXP element from a STRSXP.
650    ///
651    /// Equivalent to R's `STRING_ELT(x, i)`.
652    fn string_elt(&self, i: isize) -> SEXP;
653
654    /// Get the i-th string element as `Option<&str>`.
655    ///
656    /// Returns `None` for `NA_character_`. The returned `&str` borrows from R's
657    /// internal string cache (CHARSXP global pool) and is valid as long as the
658    /// parent STRSXP is protected from GC. The lifetime is tied to `&self` by
659    /// the borrow checker, but the true validity depends on GC protection —
660    /// do not hold the `&str` across allocation boundaries without ensuring
661    /// the SEXP remains protected.
662    fn string_elt_str(&self, i: isize) -> Option<&str>;
663
664    /// Set the i-th CHARSXP element of a STRSXP.
665    ///
666    /// Equivalent to R's `SET_STRING_ELT(x, i, v)`.
667    fn set_string_elt(&self, i: isize, charsxp: SEXP);
668
669    /// Check if this CHARSXP is `NA_character_`.
670    fn is_na_string(&self) -> bool;
671
672    // endregion
673
674    // region: List element access
675
676    /// Get the i-th element of a VECSXP (generic vector / list).
677    ///
678    /// Equivalent to R's `VECTOR_ELT(x, i)`.
679    fn vector_elt(&self, i: isize) -> SEXP;
680
681    /// Set the i-th element of a VECSXP.
682    ///
683    /// Equivalent to R's `SET_VECTOR_ELT(x, i, v)`.
684    fn set_vector_elt(&self, i: isize, val: SEXP);
685
686    // endregion
687
688    // region: Typed single-element access
689
690    /// Get the i-th integer element.
691    fn integer_elt(&self, i: isize) -> i32;
692    /// Get the i-th real element.
693    fn real_elt(&self, i: isize) -> f64;
694    /// Get the i-th logical element (raw i32: 0/1/NA_LOGICAL).
695    fn logical_elt(&self, i: isize) -> i32;
696    /// Get the i-th complex element.
697    fn complex_elt(&self, i: isize) -> Rcomplex;
698    /// Get the i-th raw element.
699    fn raw_elt(&self, i: isize) -> u8;
700
701    /// Set the i-th integer element.
702    fn set_integer_elt(&self, i: isize, v: i32);
703    /// Set the i-th real element.
704    fn set_real_elt(&self, i: isize, v: f64);
705    /// Set the i-th logical element (raw i32: 0/1/NA_LOGICAL).
706    fn set_logical_elt(&self, i: isize, v: i32);
707    /// Set the i-th complex element.
708    fn set_complex_elt(&self, i: isize, v: Rcomplex);
709    /// Set the i-th raw element.
710    fn set_raw_elt(&self, i: isize, v: u8);
711
712    // endregion
713
714    // region: Symbol and CHARSXP access
715
716    /// Get the print name (CHARSXP) of a symbol (SYMSXP).
717    ///
718    /// # Safety
719    ///
720    /// The SEXP must be a valid SYMSXP.
721    fn printname(&self) -> SEXP;
722
723    /// Get the C string pointer from a CHARSXP.
724    ///
725    /// The returned pointer is valid as long as the CHARSXP is protected.
726    ///
727    /// # Safety
728    ///
729    /// The SEXP must be a valid CHARSXP.
730    fn r_char(&self) -> *const ::std::os::raw::c_char;
731
732    /// Get a `&str` from a CHARSXP. Returns `None` for `NA_character_`.
733    fn r_char_str(&self) -> Option<&str>;
734
735    // endregion
736
737    // region: Vector resizing
738
739    /// Resize a vector to a new length, returning a (possibly new) SEXP.
740    ///
741    /// If the new length is shorter, elements are truncated.
742    /// If longer, new elements are filled with NA/NULL.
743    /// Equivalent to R's `Rf_xlengthgets(x, newlen)`.
744    fn resize(&self, newlen: R_xlen_t) -> SEXP;
745
746    // endregion
747
748    // region: Duplication
749
750    /// Deep-copy this SEXP. Equivalent to R's `Rf_duplicate(x)`.
751    fn duplicate(&self) -> SEXP;
752
753    /// Shallow-copy this SEXP. Equivalent to R's `Rf_shallow_duplicate(x)`.
754    fn shallow_duplicate(&self) -> SEXP;
755
756    // endregion
757
758    // region: Unchecked variants (bypass thread-check, for perf-critical paths)
759
760    /// Get the i-th CHARSXP from a STRSXP. No thread check.
761    ///
762    /// # Safety
763    /// Must be called from R's main thread.
764    unsafe fn string_elt_unchecked(&self, i: isize) -> SEXP;
765    /// Set the i-th CHARSXP of a STRSXP. No thread check.
766    ///
767    /// # Safety
768    /// Must be called from R's main thread.
769    unsafe fn set_string_elt_unchecked(&self, i: isize, charsxp: SEXP);
770    /// Get the i-th element of a VECSXP. No thread check.
771    ///
772    /// # Safety
773    /// Must be called from R's main thread.
774    unsafe fn vector_elt_unchecked(&self, i: isize) -> SEXP;
775    /// Set the i-th element of a VECSXP. No thread check.
776    ///
777    /// # Safety
778    /// Must be called from R's main thread.
779    unsafe fn set_vector_elt_unchecked(&self, i: isize, val: SEXP);
780    /// Get an attribute by symbol. No thread check.
781    ///
782    /// # Safety
783    /// Must be called from R's main thread.
784    unsafe fn get_attr_unchecked(&self, name: SEXP) -> SEXP;
785    /// Set an attribute by symbol. No thread check.
786    ///
787    /// # Safety
788    /// Must be called from R's main thread.
789    unsafe fn set_attr_unchecked(&self, name: SEXP, val: SEXP);
790
791    /// Get C string pointer from a CHARSXP. No thread check.
792    ///
793    /// # Safety
794    /// Must be called from R's main thread. The SEXP must be a valid CHARSXP.
795    unsafe fn r_char_unchecked(&self) -> *const ::std::os::raw::c_char;
796
797    // endregion
798}
799
800impl SexpExt for SEXP {
801    #[inline]
802    fn type_of(&self) -> SEXPTYPE {
803        unsafe { TYPEOF(*self) }
804    }
805
806    #[inline]
807    fn is_null_or_nil(&self) -> bool {
808        self.is_null() || std::ptr::addr_eq(self.0, unsafe { R_NilValue.0 })
809    }
810
811    #[inline]
812    fn len(&self) -> usize {
813        unsafe { Rf_xlength(*self) as usize }
814    }
815
816    #[inline]
817    fn xlength(&self) -> R_xlen_t {
818        unsafe { Rf_xlength(*self) }
819    }
820
821    #[inline]
822    unsafe fn len_unchecked(&self) -> usize {
823        unsafe { Rf_xlength_unchecked(*self) as usize }
824    }
825
826    #[inline]
827    unsafe fn as_slice<T: RNativeType>(&self) -> &'static [T] {
828        debug_assert!(
829            self.type_of() == T::SEXP_TYPE,
830            "SEXP type mismatch: expected {:?}, got {:?}",
831            T::SEXP_TYPE,
832            self.type_of()
833        );
834        let len = self.len();
835        if len == 0 {
836            &[]
837        } else {
838            unsafe { std::slice::from_raw_parts(DATAPTR_RO(*self).cast(), len) }
839        }
840    }
841
842    #[inline]
843    unsafe fn as_mut_slice<T: RNativeType>(&self) -> &'static mut [T] {
844        debug_assert!(
845            self.type_of() == T::SEXP_TYPE,
846            "SEXP type mismatch: expected {:?}, got {:?}",
847            T::SEXP_TYPE,
848            self.type_of()
849        );
850        let len = self.len();
851        if len == 0 {
852            &mut []
853        } else {
854            unsafe { std::slice::from_raw_parts_mut(T::dataptr_mut(*self), len) }
855        }
856    }
857
858    #[inline]
859    unsafe fn as_slice_unchecked<T: RNativeType>(&self) -> &'static [T] {
860        debug_assert!(
861            self.type_of() == T::SEXP_TYPE,
862            "SEXP type mismatch: expected {:?}, got {:?}",
863            T::SEXP_TYPE,
864            self.type_of()
865        );
866        let len = unsafe { self.len_unchecked() };
867        if len == 0 {
868            &[]
869        } else {
870            unsafe { std::slice::from_raw_parts(DATAPTR_RO_unchecked(*self).cast(), len) }
871        }
872    }
873
874    // Type checking methods
875
876    #[inline]
877    fn is_integer(&self) -> bool {
878        self.type_of() == SEXPTYPE::INTSXP
879    }
880
881    #[inline]
882    fn is_real(&self) -> bool {
883        self.type_of() == SEXPTYPE::REALSXP
884    }
885
886    #[inline]
887    fn is_logical(&self) -> bool {
888        self.type_of() == SEXPTYPE::LGLSXP
889    }
890
891    #[inline]
892    fn is_character(&self) -> bool {
893        self.type_of() == SEXPTYPE::STRSXP
894    }
895
896    #[inline]
897    fn is_raw(&self) -> bool {
898        self.type_of() == SEXPTYPE::RAWSXP
899    }
900
901    #[inline]
902    fn is_complex(&self) -> bool {
903        self.type_of() == SEXPTYPE::CPLXSXP
904    }
905
906    #[inline]
907    fn is_list(&self) -> bool {
908        self.type_of() == SEXPTYPE::VECSXP
909    }
910
911    #[inline]
912    fn is_external_ptr(&self) -> bool {
913        self.type_of() == SEXPTYPE::EXTPTRSXP
914    }
915
916    #[inline]
917    fn is_environment(&self) -> bool {
918        self.type_of() == SEXPTYPE::ENVSXP
919    }
920
921    #[inline]
922    fn is_symbol(&self) -> bool {
923        self.type_of() == SEXPTYPE::SYMSXP
924    }
925
926    #[inline]
927    fn is_language(&self) -> bool {
928        self.type_of() == SEXPTYPE::LANGSXP
929    }
930
931    #[inline]
932    fn is_altrep(&self) -> bool {
933        unsafe { ALTREP(*self) != 0 }
934    }
935
936    #[inline]
937    fn is_empty(&self) -> bool {
938        self.len() == 0
939    }
940
941    #[inline]
942    fn is_nil(&self) -> bool {
943        // Pointer comparison, not type dereference — safe on dangling pointers.
944        // R_NilValue is the singleton NILSXP; checking type_of() would crash
945        // on freed SEXPs during cleanup.
946        unsafe { std::ptr::addr_eq(self.0, R_NilValue.0) }
947    }
948
949    #[inline]
950    fn is_factor(&self) -> bool {
951        unsafe { Rf_isFactor(*self) != Rboolean::FALSE }
952    }
953
954    #[inline]
955    fn is_pair_list(&self) -> bool {
956        unsafe { Rf_isList(*self) != Rboolean::FALSE }
957    }
958
959    #[inline]
960    fn is_matrix(&self) -> bool {
961        unsafe { Rf_isMatrix(*self) != Rboolean::FALSE }
962    }
963
964    #[inline]
965    fn is_array(&self) -> bool {
966        unsafe { Rf_isArray(*self) != Rboolean::FALSE }
967    }
968
969    #[inline]
970    fn is_function(&self) -> bool {
971        unsafe { Rf_isFunction(*self) != Rboolean::FALSE }
972    }
973
974    #[inline]
975    fn is_s4(&self) -> bool {
976        unsafe { Rf_isS4(*self) != Rboolean::FALSE }
977    }
978
979    #[inline]
980    fn is_data_frame(&self) -> bool {
981        self.inherits_class(c"data.frame")
982    }
983
984    #[inline]
985    fn is_numeric(&self) -> bool {
986        let typ = self.type_of();
987        (typ == SEXPTYPE::INTSXP || typ == SEXPTYPE::LGLSXP || typ == SEXPTYPE::REALSXP)
988            && !self.is_factor()
989    }
990
991    #[inline]
992    fn is_number(&self) -> bool {
993        self.is_numeric() || self.is_complex()
994    }
995
996    #[inline]
997    fn is_vector_atomic(&self) -> bool {
998        matches!(
999            self.type_of(),
1000            SEXPTYPE::LGLSXP
1001                | SEXPTYPE::INTSXP
1002                | SEXPTYPE::REALSXP
1003                | SEXPTYPE::CPLXSXP
1004                | SEXPTYPE::STRSXP
1005                | SEXPTYPE::RAWSXP
1006        )
1007    }
1008
1009    #[inline]
1010    fn is_vector_list(&self) -> bool {
1011        let typ = self.type_of();
1012        typ == SEXPTYPE::VECSXP || typ == SEXPTYPE::EXPRSXP
1013    }
1014
1015    #[inline]
1016    fn is_vector(&self) -> bool {
1017        self.is_vector_atomic() || self.is_vector_list()
1018    }
1019
1020    #[inline]
1021    fn is_object(&self) -> bool {
1022        unsafe { Rf_isObject(*self) != Rboolean::FALSE }
1023    }
1024
1025    // region: Coercion and scalar extraction
1026
1027    #[inline]
1028    fn coerce(&self, target: SEXPTYPE) -> SEXP {
1029        unsafe { Rf_coerceVector(*self, target) }
1030    }
1031
1032    #[inline]
1033    fn as_logical(&self) -> Option<bool> {
1034        let v = unsafe { Rf_asLogical(*self) };
1035        if v == crate::altrep_traits::NA_LOGICAL {
1036            None
1037        } else {
1038            Some(v != 0)
1039        }
1040    }
1041
1042    #[inline]
1043    fn as_integer(&self) -> Option<i32> {
1044        let v = unsafe { Rf_asInteger(*self) };
1045        if v == crate::altrep_traits::NA_INTEGER {
1046            None
1047        } else {
1048            Some(v)
1049        }
1050    }
1051
1052    #[inline]
1053    fn as_real(&self) -> Option<f64> {
1054        let v = unsafe { Rf_asReal(*self) };
1055        if v.to_bits() == crate::altrep_traits::NA_REAL.to_bits() {
1056            None
1057        } else {
1058            Some(v)
1059        }
1060    }
1061
1062    #[inline]
1063    fn as_char(&self) -> SEXP {
1064        unsafe { Rf_asChar(*self) }
1065    }
1066
1067    // endregion
1068
1069    // region: Attribute access
1070
1071    #[inline]
1072    fn get_attr(&self, name: SEXP) -> SEXP {
1073        unsafe { Rf_getAttrib(*self, name) }
1074    }
1075
1076    #[inline]
1077    fn set_attr(&self, name: SEXP, val: SEXP) {
1078        unsafe {
1079            Rf_setAttrib(*self, name, val);
1080        }
1081    }
1082
1083    #[inline]
1084    fn get_names(&self) -> SEXP {
1085        unsafe { Rf_getAttrib(*self, R_NamesSymbol) }
1086    }
1087
1088    #[inline]
1089    fn set_names(&self, names: SEXP) {
1090        unsafe {
1091            Rf_namesgets(*self, names);
1092        }
1093    }
1094
1095    #[inline]
1096    fn get_class(&self) -> SEXP {
1097        unsafe { Rf_getAttrib(*self, R_ClassSymbol) }
1098    }
1099
1100    #[inline]
1101    fn set_class(&self, class: SEXP) {
1102        unsafe {
1103            Rf_classgets(*self, class);
1104        }
1105    }
1106
1107    #[inline]
1108    fn get_dim(&self) -> SEXP {
1109        unsafe { Rf_getAttrib(*self, R_DimSymbol) }
1110    }
1111
1112    #[inline]
1113    fn set_dim(&self, dim: SEXP) {
1114        unsafe {
1115            Rf_dimgets(*self, dim);
1116        }
1117    }
1118
1119    #[inline]
1120    fn get_dimnames(&self) -> SEXP {
1121        unsafe { Rf_getAttrib(*self, R_DimNamesSymbol) }
1122    }
1123
1124    #[inline]
1125    fn set_dimnames(&self, dimnames: SEXP) {
1126        unsafe {
1127            Rf_dimnamesgets(*self, dimnames);
1128        }
1129    }
1130
1131    #[inline]
1132    fn get_levels(&self) -> SEXP {
1133        unsafe { Rf_getAttrib(*self, R_LevelsSymbol) }
1134    }
1135
1136    #[inline]
1137    fn set_levels(&self, levels: SEXP) {
1138        unsafe {
1139            Rf_setAttrib(*self, R_LevelsSymbol, levels);
1140        }
1141    }
1142
1143    #[inline]
1144    fn get_row_names(&self) -> SEXP {
1145        unsafe { Rf_getAttrib(*self, R_RowNamesSymbol) }
1146    }
1147
1148    #[inline]
1149    fn set_row_names(&self, row_names: SEXP) {
1150        unsafe {
1151            Rf_setAttrib(*self, R_RowNamesSymbol, row_names);
1152        }
1153    }
1154
1155    #[inline]
1156    fn inherits_class(&self, class: &std::ffi::CStr) -> bool {
1157        unsafe { Rf_inherits(*self, class.as_ptr()) != Rboolean::FALSE }
1158    }
1159
1160    // endregion
1161
1162    // region: String element access
1163
1164    #[inline]
1165    fn string_elt(&self, i: isize) -> SEXP {
1166        unsafe { STRING_ELT(*self, i) }
1167    }
1168
1169    #[inline]
1170    fn string_elt_str(&self, i: isize) -> Option<&str> {
1171        unsafe {
1172            let charsxp = STRING_ELT(*self, i);
1173            if std::ptr::addr_eq(charsxp.0, R_NaString.0) {
1174                return None;
1175            }
1176            let p = R_CHAR(charsxp);
1177            Some(std::ffi::CStr::from_ptr(p).to_str().unwrap_or(""))
1178        }
1179    }
1180
1181    #[inline]
1182    fn set_string_elt(&self, i: isize, charsxp: SEXP) {
1183        unsafe { SET_STRING_ELT(*self, i, charsxp) }
1184    }
1185
1186    #[inline]
1187    fn is_na_string(&self) -> bool {
1188        unsafe { std::ptr::addr_eq(self.0, R_NaString.0) }
1189    }
1190
1191    // endregion
1192
1193    // region: List element access
1194
1195    #[inline]
1196    fn vector_elt(&self, i: isize) -> SEXP {
1197        unsafe { VECTOR_ELT(*self, i) }
1198    }
1199
1200    #[inline]
1201    fn set_vector_elt(&self, i: isize, val: SEXP) {
1202        unsafe {
1203            SET_VECTOR_ELT(*self, i, val);
1204        }
1205    }
1206
1207    // endregion
1208
1209    // region: Typed single-element access
1210
1211    #[inline]
1212    fn integer_elt(&self, i: isize) -> i32 {
1213        unsafe { INTEGER_ELT(*self, i) }
1214    }
1215    #[inline]
1216    fn real_elt(&self, i: isize) -> f64 {
1217        unsafe { REAL_ELT(*self, i) }
1218    }
1219    #[inline]
1220    fn logical_elt(&self, i: isize) -> i32 {
1221        unsafe { LOGICAL_ELT(*self, i) }
1222    }
1223    #[inline]
1224    fn complex_elt(&self, i: isize) -> Rcomplex {
1225        unsafe { COMPLEX_ELT(*self, i) }
1226    }
1227    #[inline]
1228    fn raw_elt(&self, i: isize) -> u8 {
1229        unsafe { RAW_ELT(*self, i) }
1230    }
1231    #[inline]
1232    fn set_integer_elt(&self, i: isize, v: i32) {
1233        unsafe { SET_INTEGER_ELT(*self, i, v) }
1234    }
1235    #[inline]
1236    fn set_real_elt(&self, i: isize, v: f64) {
1237        unsafe { SET_REAL_ELT(*self, i, v) }
1238    }
1239    #[inline]
1240    fn set_logical_elt(&self, i: isize, v: i32) {
1241        unsafe { SET_LOGICAL_ELT(*self, i, v) }
1242    }
1243    #[inline]
1244    fn set_complex_elt(&self, i: isize, v: Rcomplex) {
1245        unsafe { SET_COMPLEX_ELT(*self, i, v) }
1246    }
1247    #[inline]
1248    fn set_raw_elt(&self, i: isize, v: u8) {
1249        unsafe { SET_RAW_ELT(*self, i, v) }
1250    }
1251
1252    // endregion
1253
1254    // region: Symbol and CHARSXP access
1255
1256    #[inline]
1257    fn printname(&self) -> SEXP {
1258        unsafe { PRINTNAME(*self) }
1259    }
1260
1261    #[inline]
1262    fn r_char(&self) -> *const ::std::os::raw::c_char {
1263        unsafe { R_CHAR(*self) }
1264    }
1265
1266    #[inline]
1267    fn r_char_str(&self) -> Option<&str> {
1268        if self.is_na_string() {
1269            return None;
1270        }
1271        let p = unsafe { R_CHAR(*self) };
1272        Some(
1273            unsafe { std::ffi::CStr::from_ptr(p) }
1274                .to_str()
1275                .unwrap_or(""),
1276        )
1277    }
1278
1279    // endregion
1280
1281    // region: Vector resizing
1282
1283    #[inline]
1284    fn resize(&self, newlen: R_xlen_t) -> SEXP {
1285        unsafe { Rf_xlengthgets(*self, newlen) }
1286    }
1287
1288    // endregion
1289
1290    // region: Duplication
1291
1292    #[inline]
1293    fn duplicate(&self) -> SEXP {
1294        unsafe { Rf_duplicate(*self) }
1295    }
1296
1297    #[inline]
1298    fn shallow_duplicate(&self) -> SEXP {
1299        unsafe { Rf_shallow_duplicate(*self) }
1300    }
1301
1302    // endregion
1303
1304    // region: Unchecked variants
1305
1306    #[inline]
1307    unsafe fn string_elt_unchecked(&self, i: isize) -> SEXP {
1308        unsafe { STRING_ELT_unchecked(*self, i) }
1309    }
1310
1311    #[inline]
1312    unsafe fn set_string_elt_unchecked(&self, i: isize, charsxp: SEXP) {
1313        unsafe { SET_STRING_ELT_unchecked(*self, i, charsxp) }
1314    }
1315
1316    #[inline]
1317    unsafe fn vector_elt_unchecked(&self, i: isize) -> SEXP {
1318        unsafe { VECTOR_ELT_unchecked(*self, i) }
1319    }
1320
1321    #[inline]
1322    unsafe fn set_vector_elt_unchecked(&self, i: isize, val: SEXP) {
1323        unsafe {
1324            SET_VECTOR_ELT_unchecked(*self, i, val);
1325        }
1326    }
1327
1328    #[inline]
1329    unsafe fn get_attr_unchecked(&self, name: SEXP) -> SEXP {
1330        unsafe { Rf_getAttrib_unchecked(*self, name) }
1331    }
1332
1333    #[inline]
1334    unsafe fn set_attr_unchecked(&self, name: SEXP, val: SEXP) {
1335        unsafe {
1336            Rf_setAttrib_unchecked(*self, name, val);
1337        }
1338    }
1339
1340    #[inline]
1341    unsafe fn r_char_unchecked(&self) -> *const ::std::os::raw::c_char {
1342        unsafe { R_CHAR_unchecked(*self) }
1343    }
1344
1345    // endregion
1346}
1347
1348/// Extension trait for SEXP providing pairlist (cons cell) operations.
1349///
1350/// Pairlist nodes have three slots: CAR (value), CDR (next), and TAG (name).
1351/// This trait encapsulates the raw C functions behind method calls.
1352#[allow(dead_code)]
1353pub(crate) trait PairListExt {
1354    /// Create a cons cell with this SEXP as CAR and `cdr` as CDR.
1355    fn cons(self, cdr: SEXP) -> SEXP;
1356
1357    /// Create a language cons cell with this SEXP as CAR and `cdr` as CDR.
1358    fn lcons(self, cdr: SEXP) -> SEXP;
1359
1360    /// Get the CAR (head/value) of this pairlist node.
1361    fn car(&self) -> SEXP;
1362
1363    /// Get the CDR (tail/rest) of this pairlist node.
1364    fn cdr(&self) -> SEXP;
1365
1366    /// Get the TAG (name symbol) of this pairlist node.
1367    fn tag(&self) -> SEXP;
1368
1369    /// Set the TAG (name symbol) of this pairlist node.
1370    fn set_tag(&self, tag: SEXP);
1371
1372    /// Set the CAR (value) of this pairlist node.
1373    fn set_car(&self, value: SEXP) -> SEXP;
1374
1375    /// Set the CDR (tail) of this pairlist node.
1376    fn set_cdr(&self, tail: SEXP) -> SEXP;
1377
1378    /// Create a cons cell (no thread check).
1379    /// # Safety
1380    /// Must be called from R's main thread.
1381    unsafe fn cons_unchecked(self, cdr: SEXP) -> SEXP;
1382
1383    /// Get the CAR (no thread check).
1384    /// # Safety
1385    /// Must be called from R's main thread.
1386    unsafe fn car_unchecked(&self) -> SEXP;
1387
1388    /// Get the CDR (no thread check).
1389    /// # Safety
1390    /// Must be called from R's main thread.
1391    unsafe fn cdr_unchecked(&self) -> SEXP;
1392
1393    /// Set the TAG (no thread check).
1394    /// # Safety
1395    /// Must be called from R's main thread.
1396    unsafe fn set_tag_unchecked(&self, tag: SEXP);
1397
1398    /// Set the CAR (no thread check).
1399    /// # Safety
1400    /// Must be called from R's main thread.
1401    unsafe fn set_car_unchecked(&self, value: SEXP) -> SEXP;
1402
1403    /// Set the CDR (no thread check).
1404    /// # Safety
1405    /// Must be called from R's main thread.
1406    unsafe fn set_cdr_unchecked(&self, tail: SEXP) -> SEXP;
1407}
1408
1409impl PairListExt for SEXP {
1410    #[inline]
1411    fn cons(self, cdr: SEXP) -> SEXP {
1412        unsafe { Rf_cons(self, cdr) }
1413    }
1414
1415    #[inline]
1416    fn lcons(self, cdr: SEXP) -> SEXP {
1417        unsafe { Rf_lcons(self, cdr) }
1418    }
1419
1420    #[inline]
1421    fn car(&self) -> SEXP {
1422        unsafe { CAR(*self) }
1423    }
1424
1425    #[inline]
1426    fn cdr(&self) -> SEXP {
1427        unsafe { CDR(*self) }
1428    }
1429
1430    #[inline]
1431    fn tag(&self) -> SEXP {
1432        unsafe { TAG(*self) }
1433    }
1434
1435    #[inline]
1436    fn set_tag(&self, tag: SEXP) {
1437        unsafe { SET_TAG(*self, tag) }
1438    }
1439
1440    #[inline]
1441    fn set_car(&self, value: SEXP) -> SEXP {
1442        unsafe { SETCAR(*self, value) }
1443    }
1444
1445    #[inline]
1446    fn set_cdr(&self, tail: SEXP) -> SEXP {
1447        unsafe { SETCDR(*self, tail) }
1448    }
1449
1450    #[inline]
1451    unsafe fn cons_unchecked(self, cdr: SEXP) -> SEXP {
1452        unsafe { Rf_cons_unchecked(self, cdr) }
1453    }
1454
1455    #[inline]
1456    unsafe fn car_unchecked(&self) -> SEXP {
1457        unsafe { CAR_unchecked(*self) }
1458    }
1459
1460    #[inline]
1461    unsafe fn cdr_unchecked(&self) -> SEXP {
1462        unsafe { CDR_unchecked(*self) }
1463    }
1464
1465    #[inline]
1466    unsafe fn set_tag_unchecked(&self, tag: SEXP) {
1467        unsafe { SET_TAG_unchecked(*self, tag) }
1468    }
1469
1470    #[inline]
1471    unsafe fn set_car_unchecked(&self, value: SEXP) -> SEXP {
1472        unsafe { SETCAR_unchecked(*self, value) }
1473    }
1474
1475    #[inline]
1476    unsafe fn set_cdr_unchecked(&self, tail: SEXP) -> SEXP {
1477        unsafe { SETCDR_unchecked(*self, tail) }
1478    }
1479}
1480
1481/// Marker trait for types that correspond to R's native vector element types.
1482///
1483/// This enables blanket implementations for `TryFromSexp` and safe conversions.
1484pub trait RNativeType: Sized + Copy + 'static {
1485    /// The SEXPTYPE for vectors containing this element type.
1486    const SEXP_TYPE: SEXPTYPE;
1487
1488    /// Get mutable pointer to vector data.
1489    ///
1490    /// For empty vectors (length 0), returns an aligned dangling pointer rather than
1491    /// R's internal 0x1 sentinel, which isn't properly aligned for most types.
1492    /// This allows safe creation of zero-length slices with `std::slice::from_raw_parts_mut`.
1493    ///
1494    /// # Safety
1495    ///
1496    /// - `sexp` must be a valid, non-null SEXP of the corresponding vector type.
1497    /// - For ALTREP vectors, this may trigger materialization.
1498    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self;
1499
1500    /// Read the i-th element via the appropriate `*_ELT` accessor.
1501    ///
1502    /// Goes through R's ALTREP dispatch for ALTREP vectors.
1503    fn elt(sexp: SEXP, i: isize) -> Self;
1504}
1505
1506/// R's logical element type (the contents of a `LGLSXP` vector).
1507///
1508/// In R, logical vectors are stored as `int` with possible values:
1509/// - `0` for FALSE
1510/// - `1` for TRUE
1511/// - `NA_LOGICAL` (typically `INT_MIN`) for NA
1512///
1513/// **Important:** R may also contain other non-zero values in logical vectors
1514/// (e.g., from low-level code). Those should be interpreted as TRUE.
1515///
1516/// This type is `repr(transparent)` over `i32` so *any* raw value is valid,
1517/// avoiding UB when viewing `LGLSXP` data as a slice.
1518#[repr(transparent)]
1519#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1520pub struct RLogical(i32);
1521
1522impl RLogical {
1523    /// FALSE logical scalar.
1524    pub const FALSE: Self = Self(0);
1525    /// TRUE logical scalar.
1526    pub const TRUE: Self = Self(1);
1527    /// Missing logical scalar (`NA_LOGICAL`).
1528    pub const NA: Self = Self(i32::MIN);
1529
1530    /// Construct directly from raw R logical storage.
1531    #[inline]
1532    pub const fn from_i32(raw: i32) -> Self {
1533        Self(raw)
1534    }
1535
1536    /// Get raw R logical storage value.
1537    #[inline]
1538    pub const fn to_i32(self) -> i32 {
1539        self.0
1540    }
1541
1542    /// Returns whether the value is `NA_LOGICAL`.
1543    #[inline]
1544    pub const fn is_na(self) -> bool {
1545        self.0 == i32::MIN
1546    }
1547
1548    /// Convert to Rust `Option<bool>` (`None` for `NA`).
1549    #[inline]
1550    pub const fn to_option_bool(self) -> Option<bool> {
1551        match self.0 {
1552            0 => Some(false),
1553            i32::MIN => None,
1554            _ => Some(true),
1555        }
1556    }
1557}
1558
1559impl From<bool> for RLogical {
1560    #[inline]
1561    fn from(value: bool) -> Self {
1562        if value { Self::TRUE } else { Self::FALSE }
1563    }
1564}
1565
1566impl RNativeType for i32 {
1567    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::INTSXP;
1568
1569    #[inline]
1570    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
1571        unsafe {
1572            if Rf_xlength(sexp) == 0 {
1573                std::ptr::NonNull::<Self>::dangling().as_ptr()
1574            } else {
1575                INTEGER(sexp)
1576            }
1577        }
1578    }
1579
1580    #[inline]
1581    fn elt(sexp: SEXP, i: isize) -> Self {
1582        unsafe { INTEGER_ELT(sexp, i) }
1583    }
1584}
1585
1586impl RNativeType for f64 {
1587    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::REALSXP;
1588
1589    #[inline]
1590    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
1591        unsafe {
1592            if Rf_xlength(sexp) == 0 {
1593                std::ptr::NonNull::<Self>::dangling().as_ptr()
1594            } else {
1595                REAL(sexp)
1596            }
1597        }
1598    }
1599
1600    #[inline]
1601    fn elt(sexp: SEXP, i: isize) -> Self {
1602        unsafe { REAL_ELT(sexp, i) }
1603    }
1604}
1605
1606impl RNativeType for u8 {
1607    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::RAWSXP;
1608
1609    #[inline]
1610    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
1611        unsafe {
1612            if Rf_xlength(sexp) == 0 {
1613                std::ptr::NonNull::<Self>::dangling().as_ptr()
1614            } else {
1615                RAW(sexp)
1616            }
1617        }
1618    }
1619
1620    #[inline]
1621    fn elt(sexp: SEXP, i: isize) -> Self {
1622        unsafe { RAW_ELT(sexp, i) }
1623    }
1624}
1625
1626impl RNativeType for RLogical {
1627    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::LGLSXP;
1628
1629    #[inline]
1630    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
1631        // LOGICAL returns *mut c_int, RLogical is repr(transparent) over i32
1632        unsafe {
1633            if Rf_xlength(sexp) == 0 {
1634                std::ptr::NonNull::<Self>::dangling().as_ptr()
1635            } else {
1636                LOGICAL(sexp).cast()
1637            }
1638        }
1639    }
1640
1641    #[inline]
1642    fn elt(sexp: SEXP, i: isize) -> Self {
1643        RLogical(unsafe { LOGICAL_ELT(sexp, i) })
1644    }
1645}
1646
1647impl RNativeType for Rcomplex {
1648    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::CPLXSXP;
1649
1650    #[inline]
1651    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
1652        unsafe {
1653            if Rf_xlength(sexp) == 0 {
1654                std::ptr::NonNull::<Self>::dangling().as_ptr()
1655            } else {
1656                COMPLEX(sexp)
1657            }
1658        }
1659    }
1660
1661    #[inline]
1662    fn elt(sexp: SEXP, i: isize) -> Self {
1663        unsafe { COMPLEX_ELT(sexp, i) }
1664    }
1665}
1666
1667#[repr(i32)]
1668#[non_exhaustive]
1669#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1670/// Binary boolean used by many R C APIs.
1671pub enum Rboolean {
1672    /// False.
1673    FALSE = 0,
1674    /// True.
1675    TRUE = 1,
1676}
1677
1678impl From<bool> for Rboolean {
1679    fn from(value: bool) -> Self {
1680        match value {
1681            true => Rboolean::TRUE,
1682            false => Rboolean::FALSE,
1683        }
1684    }
1685}
1686
1687impl From<Rboolean> for bool {
1688    fn from(value: Rboolean) -> Self {
1689        match value {
1690            Rboolean::FALSE => false,
1691            Rboolean::TRUE => true,
1692        }
1693    }
1694}
1695
1696#[allow(non_camel_case_types)]
1697/// C finalizer callback signature used by external pointers.
1698pub type R_CFinalizer_t = ::std::option::Option<unsafe extern "C-unwind" fn(s: SEXP)>;
1699
1700#[repr(C)]
1701#[derive(Copy, Clone)]
1702#[allow(non_camel_case_types)]
1703/// Character encoding tag used by CHARSXP constructors.
1704pub enum cetype_t {
1705    /// Native locale encoding.
1706    CE_NATIVE = 0,
1707    /// UTF-8 encoding.
1708    CE_UTF8 = 1,
1709    /// Latin-1 encoding.
1710    CE_LATIN1 = 2,
1711    /// Raw bytes encoding.
1712    CE_BYTES = 3,
1713    /// Symbol encoding marker.
1714    CE_SYMBOL = 5,
1715    /// Any encoding accepted.
1716    CE_ANY = 99,
1717}
1718pub use cetype_t::CE_UTF8;
1719
1720// region: Connections types (gated behind `connections` feature)
1721// WARNING: R's connection API is explicitly marked as UNSTABLE.
1722
1723/// Opaque R connection implementation (from R_ext/Connections.h).
1724///
1725/// This is an opaque type representing R's internal connection structure.
1726/// The actual structure is explicitly unstable and may change between R versions.
1727#[cfg(feature = "connections")]
1728#[repr(C)]
1729#[allow(non_camel_case_types)]
1730pub struct Rconnection_impl(::std::os::raw::c_void);
1731
1732/// Pointer to an R connection handle.
1733///
1734/// This is the typed equivalent of R's `Rconnection` type, which is a pointer
1735/// to the opaque `Rconn` struct. Using this instead of `*mut c_void` provides
1736/// type safety for connection APIs.
1737#[cfg(feature = "connections")]
1738#[allow(non_camel_case_types)]
1739pub type Rconnection = *mut Rconnection_impl;
1740
1741/// R connections API version from R's `R_ext/Connections.h` at compile time.
1742///
1743/// This is a compile-time constant baked into the Rust FFI bindings when they
1744/// were generated against a particular R version's headers. It does **not**
1745/// dynamically probe the running R session.
1746///
1747/// From R_ext/Connections.h: "you *must* check the version and proceed only
1748/// if it matches what you expect. We explicitly reserve the right to change
1749/// the connection implementation without a compatibility layer."
1750///
1751/// Before using any connection APIs, check that this equals the expected version (1).
1752#[cfg(feature = "connections")]
1753#[allow(non_upper_case_globals)]
1754pub const R_CONNECTIONS_VERSION: ::std::os::raw::c_int = 1;
1755
1756// endregion
1757
1758use miniextendr_macros::r_ffi_checked;
1759
1760// Unchecked variadic functions (internal use only, no thread check)
1761#[allow(clashing_extern_declarations)]
1762#[allow(varargs_without_pattern)]
1763unsafe extern "C-unwind" {
1764    /// Unchecked variadic `Rf_error`; call checked wrapper when possible.
1765    #[link_name = "Rf_error"]
1766    pub fn Rf_error_unchecked(arg1: *const ::std::os::raw::c_char, ...) -> !;
1767    /// Unchecked variadic `Rf_errorcall`; call checked wrapper when possible.
1768    #[link_name = "Rf_errorcall"]
1769    pub fn Rf_errorcall_unchecked(arg1: SEXP, arg2: *const ::std::os::raw::c_char, ...) -> !;
1770    /// Unchecked variadic `Rf_warning`; call checked wrapper when possible.
1771    #[link_name = "Rf_warning"]
1772    pub fn Rf_warning_unchecked(arg1: *const ::std::os::raw::c_char, ...);
1773    /// Unchecked variadic `Rprintf`; call checked wrapper when possible.
1774    #[link_name = "Rprintf"]
1775    pub fn Rprintf_unchecked(arg1: *const ::std::os::raw::c_char, ...);
1776    /// Unchecked variadic `REprintf`; call checked wrapper when possible.
1777    #[link_name = "REprintf"]
1778    pub fn REprintf_unchecked(arg1: *const ::std::os::raw::c_char, ...);
1779}
1780
1781// Error message access (non-API, declared in Rinternals.h but flagged by R CMD check)
1782#[cfg(feature = "nonapi")]
1783unsafe extern "C-unwind" {
1784    /// Get the current R error message buffer.
1785    ///
1786    /// Returns a pointer to R's internal error message buffer.
1787    /// Used by Rserve and other embedding applications.
1788    ///
1789    /// # Safety
1790    ///
1791    /// - The returned pointer is only valid until the next R error
1792    /// - Must not be modified
1793    /// - Should be copied if needed beyond the immediate scope
1794    ///
1795    /// # Feature Gate
1796    ///
1797    /// This is a non-API function and requires the `nonapi` feature.
1798    #[allow(non_snake_case, dead_code)] // used by worker.rs under worker-thread feature
1799    pub(crate) fn R_curErrorBuf() -> *const ::std::os::raw::c_char;
1800}
1801
1802// Console hooks (non-API; declared in Rinterface.h)
1803#[cfg(feature = "nonapi")]
1804unsafe extern "C-unwind" {
1805    #[expect(dead_code, reason = "declared for future use")]
1806    pub(crate) static ptr_R_WriteConsoleEx: Option<
1807        unsafe extern "C-unwind" fn(
1808            *const ::std::os::raw::c_char,
1809            ::std::os::raw::c_int,
1810            ::std::os::raw::c_int,
1811        ),
1812    >;
1813}
1814
1815/// Checked wrapper for `Rf_error` - panics if called from non-main thread.
1816/// Common usage: `Rf_error(c"%s".as_ptr(), message.as_ptr())`
1817///
1818/// # Safety
1819///
1820/// - Must be called from the R main thread
1821/// - `fmt` and `arg1` must be valid null-terminated C strings
1822#[inline(always)]
1823#[allow(non_snake_case)]
1824pub unsafe fn Rf_error(
1825    fmt: *const ::std::os::raw::c_char,
1826    arg1: *const ::std::os::raw::c_char,
1827) -> ! {
1828    if !crate::worker::is_r_main_thread() {
1829        panic!("Rf_error called from non-main thread");
1830    }
1831    unsafe { Rf_error_unchecked(fmt, arg1) }
1832}
1833
1834/// Checked wrapper for `Rf_errorcall` - panics if called from non-main thread.
1835///
1836/// # Safety
1837///
1838/// - Must be called from the R main thread
1839/// - `call` must be a valid SEXP or R_NilValue
1840/// - `fmt` and `arg1` must be valid null-terminated C strings
1841#[inline(always)]
1842#[allow(non_snake_case)]
1843pub unsafe fn Rf_errorcall(
1844    call: SEXP,
1845    fmt: *const ::std::os::raw::c_char,
1846    arg1: *const ::std::os::raw::c_char,
1847) -> ! {
1848    if !crate::worker::is_r_main_thread() {
1849        panic!("Rf_errorcall called from non-main thread");
1850    }
1851    unsafe { Rf_errorcall_unchecked(call, fmt, arg1) }
1852}
1853
1854/// Checked wrapper for `Rf_warning` - panics if called from non-main thread.
1855///
1856/// # Safety
1857///
1858/// - Must be called from the R main thread
1859/// - `fmt` and `arg1` must be valid null-terminated C strings
1860#[inline(always)]
1861#[allow(non_snake_case)]
1862pub unsafe fn Rf_warning(fmt: *const ::std::os::raw::c_char, arg1: *const ::std::os::raw::c_char) {
1863    if !crate::worker::is_r_main_thread() {
1864        panic!("Rf_warning called from non-main thread");
1865    }
1866    unsafe { Rf_warning_unchecked(fmt, arg1) }
1867}
1868
1869/// Checked wrapper for `Rprintf` - panics if called from non-main thread.
1870///
1871/// # Safety
1872///
1873/// - Must be called from the R main thread
1874/// - `fmt` and `arg1` must be valid null-terminated C strings
1875#[inline(always)]
1876#[allow(non_snake_case)]
1877pub unsafe fn Rprintf(fmt: *const ::std::os::raw::c_char, arg1: *const ::std::os::raw::c_char) {
1878    if !crate::worker::is_r_main_thread() {
1879        panic!("Rprintf called from non-main thread");
1880    }
1881    unsafe { Rprintf_unchecked(fmt, arg1) }
1882}
1883
1884/// Print to R's stderr (via R_ShowMessage or error console).
1885///
1886/// # Safety
1887///
1888/// - Must be called from the R main thread
1889/// - `fmt` and `arg1` must be valid null-terminated C strings
1890#[inline(always)]
1891#[allow(non_snake_case)]
1892pub unsafe fn REprintf(fmt: *const ::std::os::raw::c_char, arg1: *const ::std::os::raw::c_char) {
1893    if !crate::worker::is_r_main_thread() {
1894        panic!("REprintf called from non-main thread");
1895    }
1896    unsafe { REprintf_unchecked(fmt, arg1) }
1897}
1898
1899// Imported R symbols and functions with runtime thread checks enabled.
1900#[allow(missing_docs)]
1901#[r_ffi_checked]
1902#[allow(clashing_extern_declarations)]
1903unsafe extern "C-unwind" {
1904    /// The canonical R `NULL` value.
1905    pub static R_NilValue: SEXP;
1906
1907    #[doc(alias = "NA_STRING")]
1908    /// Missing string singleton — encapsulated by SEXP::na_string()
1909    static R_NaString: SEXP;
1910    /// Empty string CHARSXP — encapsulated by SEXP::blank_string()
1911    static R_BlankString: SEXP;
1912    /// Symbol for `names` attribute.
1913    // Attribute symbols — encapsulated by SexpExt methods and SEXP::*_symbol()
1914    static R_NamesSymbol: SEXP;
1915    static R_DimSymbol: SEXP;
1916    static R_DimNamesSymbol: SEXP;
1917    static R_ClassSymbol: SEXP;
1918    static R_RowNamesSymbol: SEXP;
1919    static R_LevelsSymbol: SEXP;
1920    static R_TspSymbol: SEXP;
1921
1922    /// Global environment (`.GlobalEnv`).
1923    pub static R_GlobalEnv: SEXP;
1924    /// Base package namespace environment.
1925    pub static R_BaseEnv: SEXP;
1926    /// Empty root environment.
1927    pub static R_EmptyEnv: SEXP;
1928    /// Base package namespace — encapsulated by SEXP::base_namespace()
1929    static R_BaseNamespace: SEXP;
1930
1931    /// The "missing argument" sentinel value.
1932    ///
1933    /// When an R function is called without providing a value for a formal
1934    /// argument, R passes `R_MissingArg` as a placeholder. This is different
1935    /// from `R_NilValue` (NULL) - a missing argument means "not provided",
1936    /// while NULL is an explicit value.
1937    ///
1938    /// In R: `f <- function(x) missing(x); f()` returns `TRUE`.
1939    /// Encapsulated by SEXP::missing_arg()
1940    static R_MissingArg: SEXP;
1941
1942    // Rinterface.h
1943    pub(crate) fn R_FlushConsole();
1944
1945    // Special logical values (from internal Defn.h, not public API)
1946    // These are gated behind `nonapi` feature as they may change across R versions.
1947    #[cfg(feature = "nonapi")]
1948    #[expect(dead_code, reason = "declared for future use")]
1949    /// Non-API TRUE singleton.
1950    static R_TrueValue: SEXP;
1951    #[cfg(feature = "nonapi")]
1952    #[expect(dead_code, reason = "declared for future use")]
1953    /// Non-API FALSE singleton.
1954    static R_FalseValue: SEXP;
1955    #[cfg(feature = "nonapi")]
1956    #[expect(dead_code, reason = "declared for future use")]
1957    /// Non-API NA logical singleton.
1958    static R_LogicalNAValue: SEXP;
1959
1960    // Rinternals.h
1961    #[doc(alias = "mkChar")]
1962    fn Rf_mkChar(s: *const ::std::os::raw::c_char) -> SEXP;
1963    #[doc(alias = "mkCharLen")]
1964    fn Rf_mkCharLen(s: *const ::std::os::raw::c_char, len: i32) -> SEXP;
1965    #[doc(alias = "mkCharLenCE")]
1966    pub fn Rf_mkCharLenCE(
1967        x: *const ::std::os::raw::c_char,
1968        len: ::std::os::raw::c_int,
1969        ce: cetype_t,
1970    ) -> SEXP;
1971    #[doc(alias = "xlength")]
1972    #[doc(alias = "XLENGTH")]
1973    pub(crate) fn Rf_xlength(x: SEXP) -> R_xlen_t;
1974    #[doc(alias = "translateCharUTF8")]
1975    pub fn Rf_translateCharUTF8(x: SEXP) -> *const ::std::os::raw::c_char;
1976    #[doc(alias = "getCharCE")]
1977    fn Rf_getCharCE(x: SEXP) -> cetype_t;
1978    #[doc(alias = "charIsASCII")]
1979    fn Rf_charIsASCII(x: SEXP) -> Rboolean;
1980    #[doc(alias = "charIsUTF8")]
1981    fn Rf_charIsUTF8(x: SEXP) -> Rboolean;
1982    #[doc(alias = "charIsLatin1")]
1983    fn Rf_charIsLatin1(x: SEXP) -> Rboolean;
1984
1985    pub(crate) fn R_MakeUnwindCont() -> SEXP;
1986    pub(crate) fn R_ContinueUnwind(cont: SEXP) -> !;
1987    pub(crate) fn R_UnwindProtect(
1988        fun: ::std::option::Option<
1989            unsafe extern "C-unwind" fn(*mut ::std::os::raw::c_void) -> SEXP,
1990        >,
1991        fun_data: *mut ::std::os::raw::c_void,
1992        cleanfun: ::std::option::Option<
1993            unsafe extern "C-unwind" fn(*mut ::std::os::raw::c_void, Rboolean),
1994        >,
1995        cleanfun_data: *mut ::std::os::raw::c_void,
1996        cont: SEXP,
1997    ) -> SEXP;
1998
1999    /// Version of `R_UnwindProtect` that accepts `extern "C-unwind"` function pointers
2000    #[link_name = "R_UnwindProtect"]
2001    pub(crate) fn R_UnwindProtect_C_unwind(
2002        fun: ::std::option::Option<
2003            unsafe extern "C-unwind" fn(*mut ::std::os::raw::c_void) -> SEXP,
2004        >,
2005        fun_data: *mut ::std::os::raw::c_void,
2006        cleanfun: ::std::option::Option<
2007            unsafe extern "C-unwind" fn(*mut ::std::os::raw::c_void, Rboolean),
2008        >,
2009        cleanfun_data: *mut ::std::os::raw::c_void,
2010        cont: SEXP,
2011    ) -> SEXP;
2012
2013    // Rinternals.h
2014    #[doc = " External pointer interface"]
2015    pub(crate) fn R_MakeExternalPtr(p: *mut ::std::os::raw::c_void, tag: SEXP, prot: SEXP) -> SEXP;
2016    pub fn R_ExternalPtrAddr(s: SEXP) -> *mut ::std::os::raw::c_void;
2017    pub(crate) fn R_ExternalPtrTag(s: SEXP) -> SEXP;
2018    pub(crate) fn R_ExternalPtrProtected(s: SEXP) -> SEXP;
2019    pub(crate) fn R_ClearExternalPtr(s: SEXP);
2020    pub(crate) fn R_SetExternalPtrAddr(s: SEXP, p: *mut ::std::os::raw::c_void);
2021    pub(crate) fn R_SetExternalPtrTag(s: SEXP, tag: SEXP);
2022    pub(crate) fn R_SetExternalPtrProtected(s: SEXP, p: SEXP);
2023    #[doc = " Added in R 3.4.0"]
2024    fn R_MakeExternalPtrFn(p: DL_FUNC, tag: SEXP, prot: SEXP) -> SEXP;
2025    fn R_ExternalPtrAddrFn(s: SEXP) -> DL_FUNC;
2026    fn R_RegisterFinalizer(s: SEXP, fun: SEXP);
2027    pub(crate) fn R_RegisterCFinalizer(s: SEXP, fun: R_CFinalizer_t);
2028    fn R_RegisterFinalizerEx(s: SEXP, fun: SEXP, onexit: Rboolean);
2029    pub(crate) fn R_RegisterCFinalizerEx(s: SEXP, fun: R_CFinalizer_t, onexit: Rboolean);
2030
2031    // R_ext/Rdynload.h - C-callable interface
2032    /// Register a C-callable function for cross-package access.
2033    pub(crate) fn R_RegisterCCallable(
2034        package: *const ::std::os::raw::c_char,
2035        name: *const ::std::os::raw::c_char,
2036        fptr: DL_FUNC,
2037    );
2038    /// Get a C-callable function from another package.
2039    pub(crate) fn R_GetCCallable(
2040        package: *const ::std::os::raw::c_char,
2041        name: *const ::std::os::raw::c_char,
2042    ) -> DL_FUNC;
2043
2044    // region: GC protection
2045    //
2046    // R has two GC protection mechanisms with very different cost profiles:
2047    //
2048    // ## Protect stack (`Rf_protect` / `Rf_unprotect`)
2049    //
2050    // A pre-allocated array (`R_PPStack`) with an integer index (`R_PPStackTop`).
2051    // Protect pushes: `R_PPStack[R_PPStackTop++] = s`.
2052    // Unprotect pops: `R_PPStackTop -= n`.
2053    // **No heap allocation. No GC pressure. Essentially a single memory write.**
2054    // Use this for temporary protection within a function.
2055    // Requires LIFO discipline — nested scopes are fine, interleaved are not.
2056    //
2057    // ## Precious list (`R_PreserveObject` / `R_ReleaseObject`)
2058    //
2059    // A global linked list of CONSXP cells (`R_PreciousList`).
2060    // Preserve: `CONS(object, R_PreciousList)` — **allocates a cons cell every call**.
2061    // Release: linear scan of the entire list to find and unlink the object — **O(n)**.
2062    // (Optional `R_HASH_PRECIOUS` env var enables a 1069-bucket hash table, improving
2063    // Release to O(bucket_size), but Preserve still allocates.)
2064    // Use this only for long-lived objects that outlive any single protect scope.
2065    //
2066    // ## Cost summary
2067    //
2068    // | Operation            | Cost               | Allocates? |
2069    // |----------------------|--------------------|------------|
2070    // | `Rf_protect`         | array write        | no         |
2071    // | `Rf_unprotect(n)`    | integer subtract   | no         |
2072    // | `Rf_unprotect_ptr`   | scan + shift       | no         |
2073    // | `R_PreserveObject`   | cons cell alloc    | **yes**    |
2074    // | `R_ReleaseObject`    | linked list scan   | no (O(n))  |
2075    // | `R_ProtectWithIndex` | array write + save | no         |
2076    // | `R_Reprotect`        | array index write  | no         |
2077
2078    /// Add a SEXP to the protect stack, preventing GC collection.
2079    ///
2080    /// **Cost: O(1)** — single array write (`R_PPStack[top++] = s`). No allocation.
2081    ///
2082    /// Must be balanced by a corresponding `Rf_unprotect`. The protect stack is
2083    /// LIFO — nested scopes are safe, but interleaved usage from different scopes
2084    /// will cause incorrect unprotection.
2085    #[doc(alias = "PROTECT")]
2086    #[doc(alias = "protect")]
2087    pub fn Rf_protect(s: SEXP) -> SEXP;
2088
2089    /// Pop the top `l` entries from the protect stack.
2090    ///
2091    /// **Cost: O(1)** — single integer subtract (`R_PPStackTop -= l`). No allocation.
2092    ///
2093    /// The popped SEXPs become eligible for GC. Must match the number of
2094    /// `Rf_protect` calls in the current scope (LIFO order).
2095    #[doc(alias = "UNPROTECT")]
2096    #[doc(alias = "unprotect")]
2097    pub fn Rf_unprotect(l: ::std::os::raw::c_int);
2098
2099    /// Remove a specific SEXP from anywhere in the protect stack.
2100    ///
2101    /// **Cost: O(k)** — scans backwards from top (k = distance from top), then
2102    /// shifts remaining entries down. No allocation. R source comment:
2103    /// *"should be among the top few items"*.
2104    ///
2105    /// Unlike `Rf_unprotect`, this is order-independent — it finds and removes
2106    /// the specific pointer regardless of stack position. Useful when LIFO
2107    /// discipline cannot be maintained, but more expensive than `Rf_unprotect`.
2108    #[doc(alias = "UNPROTECT_PTR")]
2109    pub fn Rf_unprotect_ptr(s: SEXP);
2110
2111    /// Add a SEXP to the global precious list, preventing GC indefinitely.
2112    ///
2113    /// **Cost: O(1) but allocates a CONSXP cell** — creates GC pressure on every
2114    /// call. The precious list is a global linked list (`R_PreciousList`).
2115    ///
2116    /// Use only for long-lived objects (e.g., ExternalPtr stored across R calls).
2117    /// For temporary protection within a function, prefer `Rf_protect`.
2118    pub fn R_PreserveObject(object: SEXP);
2119
2120    /// Remove a SEXP from the global precious list, allowing GC.
2121    ///
2122    /// **Cost: O(n)** — linear scan of the entire precious list to find and unlink
2123    /// the cons cell. With `R_HASH_PRECIOUS` env var, O(bucket_size) average
2124    /// via a 1069-bucket hash table, but this is off by default.
2125    pub fn R_ReleaseObject(object: SEXP);
2126
2127    // endregion
2128    // Vector allocation functions
2129    #[doc(alias = "allocVector")]
2130    pub fn Rf_allocVector(sexptype: SEXPTYPE, length: R_xlen_t) -> SEXP;
2131    #[doc(alias = "allocMatrix")]
2132    pub fn Rf_allocMatrix(
2133        sexptype: SEXPTYPE,
2134        nrow: ::std::os::raw::c_int,
2135        ncol: ::std::os::raw::c_int,
2136    ) -> SEXP;
2137    #[doc(alias = "allocArray")]
2138    fn Rf_allocArray(sexptype: SEXPTYPE, dims: SEXP) -> SEXP;
2139    #[doc(alias = "alloc3DArray")]
2140    fn Rf_alloc3DArray(
2141        sexptype: SEXPTYPE,
2142        nrow: ::std::os::raw::c_int,
2143        ncol: ::std::os::raw::c_int,
2144        nface: ::std::os::raw::c_int,
2145    ) -> SEXP;
2146
2147    // Pairlist allocation
2148    #[doc(alias = "allocList")]
2149    pub(crate) fn Rf_allocList(n: ::std::os::raw::c_int) -> SEXP;
2150    #[doc(alias = "allocLang")]
2151    fn Rf_allocLang(n: ::std::os::raw::c_int) -> SEXP;
2152    #[doc(alias = "allocS4Object")]
2153    fn Rf_allocS4Object() -> SEXP;
2154    #[doc(alias = "allocSExp")]
2155    fn Rf_allocSExp(sexptype: SEXPTYPE) -> SEXP;
2156
2157    // Pairlist construction — encapsulated by PairListExt trait
2158    fn Rf_cons(car: SEXP, cdr: SEXP) -> SEXP;
2159    fn Rf_lcons(car: SEXP, cdr: SEXP) -> SEXP;
2160
2161    // Attribute manipulation — encapsulated by SexpExt methods
2162    #[doc(alias = "setAttrib")]
2163    fn Rf_setAttrib(vec: SEXP, name: SEXP, val: SEXP) -> SEXP;
2164
2165    // Rinternals.h
2166    #[doc(alias = "ScalarComplex")]
2167    pub(crate) fn Rf_ScalarComplex(x: Rcomplex) -> SEXP;
2168    #[doc(alias = "ScalarInteger")]
2169    pub(crate) fn Rf_ScalarInteger(x: ::std::os::raw::c_int) -> SEXP;
2170    #[doc(alias = "ScalarLogical")]
2171    pub(crate) fn Rf_ScalarLogical(x: ::std::os::raw::c_int) -> SEXP;
2172    #[doc(alias = "ScalarRaw")]
2173    pub(crate) fn Rf_ScalarRaw(x: Rbyte) -> SEXP;
2174    #[doc(alias = "ScalarReal")]
2175    pub(crate) fn Rf_ScalarReal(x: f64) -> SEXP;
2176    #[doc(alias = "ScalarString")]
2177    pub(crate) fn Rf_ScalarString(x: SEXP) -> SEXP;
2178
2179    // Rinternals.h
2180    /// Non-API function - use DATAPTR_RO or DATAPTR_OR_NULL instead.
2181    /// Only available with `nonapi` feature.
2182    #[cfg(feature = "nonapi")]
2183    pub(crate) fn DATAPTR(x: SEXP) -> *mut ::std::os::raw::c_void;
2184    pub fn DATAPTR_RO(x: SEXP) -> *const ::std::os::raw::c_void;
2185    fn DATAPTR_OR_NULL(x: SEXP) -> *const ::std::os::raw::c_void;
2186
2187    // region: Cons Cell (Pairlist) Accessors
2188    //
2189    // R's pairlists (LISTSXP) are cons cells like in Lisp/Scheme. Each node has:
2190    // - CAR: The value/head element
2191    // - CDR: The rest/tail of the list (another pairlist or R_NilValue)
2192    // - TAG: An optional name (symbol) for named lists/arguments
2193    //
2194    // Example R pairlist: list(a = 1, b = 2, 3)
2195    // - First node:  CAR=1,    TAG="a",  CDR=<next node>
2196    // - Second node: CAR=2,    TAG="b",  CDR=<next node>
2197    // - Third node:  CAR=3,    TAG=NULL, CDR=R_NilValue
2198    //
2199    // Pairlists are used for:
2200    // - Function arguments (formal parameters and actual arguments)
2201    // - Language objects (calls)
2202    // - Dotted pairs in old-style lists
2203    //
2204    // The names CAR/CDR come from Lisp:
2205    // - CAR = "Contents of Address part of Register"
2206    // - CDR = "Contents of Decrement part of Register" (pronounced "could-er")
2207    //
2208    // Modern R mostly uses generic vectors (VECSXP) instead of pairlists,
2209    // but pairlists are still used internally for function calls.
2210
2211    // Pairlist accessors — basic ops encapsulated by PairListExt trait,
2212    // compound accessors (CAAR, CADR, etc.) module-private since no callers exist.
2213    fn CAR(e: SEXP) -> SEXP;
2214    fn CDR(e: SEXP) -> SEXP;
2215    fn CAAR(e: SEXP) -> SEXP;
2216    fn CDAR(e: SEXP) -> SEXP;
2217    fn CADR(e: SEXP) -> SEXP;
2218    fn CDDR(e: SEXP) -> SEXP;
2219    fn CADDR(e: SEXP) -> SEXP;
2220    fn CADDDR(e: SEXP) -> SEXP;
2221    fn CAD4R(e: SEXP) -> SEXP;
2222    fn TAG(e: SEXP) -> SEXP;
2223    fn SET_TAG(x: SEXP, y: SEXP);
2224    fn SETCAR(x: SEXP, y: SEXP) -> SEXP;
2225    fn SETCDR(x: SEXP, y: SEXP) -> SEXP;
2226    fn SETCADR(x: SEXP, y: SEXP) -> SEXP;
2227    fn SETCADDR(x: SEXP, y: SEXP) -> SEXP;
2228    fn SETCADDDR(x: SEXP, y: SEXP) -> SEXP;
2229    fn SETCAD4R(e: SEXP, y: SEXP) -> SEXP;
2230    fn LOGICAL_OR_NULL(x: SEXP) -> *const ::std::os::raw::c_int;
2231    fn INTEGER_OR_NULL(x: SEXP) -> *const ::std::os::raw::c_int;
2232    fn REAL_OR_NULL(x: SEXP) -> *const f64;
2233    fn COMPLEX_OR_NULL(x: SEXP) -> *const Rcomplex;
2234    fn RAW_OR_NULL(x: SEXP) -> *const Rbyte;
2235
2236    // Element-wise accessors (ALTREP-aware) — encapsulated by SexpExt methods
2237    fn INTEGER_ELT(x: SEXP, i: R_xlen_t) -> ::std::os::raw::c_int;
2238    fn REAL_ELT(x: SEXP, i: R_xlen_t) -> f64;
2239    fn LOGICAL_ELT(x: SEXP, i: R_xlen_t) -> ::std::os::raw::c_int;
2240    fn COMPLEX_ELT(x: SEXP, i: R_xlen_t) -> Rcomplex;
2241    fn RAW_ELT(x: SEXP, i: R_xlen_t) -> Rbyte;
2242    fn VECTOR_ELT(x: SEXP, i: R_xlen_t) -> SEXP;
2243    fn STRING_ELT(x: SEXP, i: R_xlen_t) -> SEXP;
2244    fn SET_STRING_ELT(x: SEXP, i: R_xlen_t, v: SEXP);
2245    fn SET_LOGICAL_ELT(x: SEXP, i: R_xlen_t, v: ::std::os::raw::c_int);
2246    fn SET_INTEGER_ELT(x: SEXP, i: R_xlen_t, v: ::std::os::raw::c_int);
2247    fn SET_REAL_ELT(x: SEXP, i: R_xlen_t, v: f64);
2248    fn SET_COMPLEX_ELT(x: SEXP, i: R_xlen_t, v: Rcomplex);
2249    fn SET_RAW_ELT(x: SEXP, i: R_xlen_t, v: Rbyte);
2250    fn SET_VECTOR_ELT(x: SEXP, i: R_xlen_t, v: SEXP) -> SEXP;
2251
2252    // endregion
2253
2254    // region: SEXP metadata accessors
2255
2256    /// Get the length of a SEXP as `int` (for short vectors < 2^31).
2257    ///
2258    /// For long vectors, use `Rf_xlength()` instead.
2259    /// Returns 0 for R_NilValue.
2260    fn LENGTH(x: SEXP) -> ::std::os::raw::c_int;
2261
2262    /// Get the length of a SEXP as `R_xlen_t` (supports long vectors).
2263    ///
2264    /// ALTREP-aware: will call ALTREP Length method if needed.
2265    fn XLENGTH(x: SEXP) -> R_xlen_t;
2266
2267    /// Get the true length (allocated capacity) of a vector.
2268    ///
2269    /// May be larger than LENGTH for vectors with reserved space.
2270    /// ALTREP-aware.
2271    fn TRUELENGTH(x: SEXP) -> R_xlen_t;
2272
2273    /// Get the attributes pairlist of a SEXP.
2274    ///
2275    /// Returns R_NilValue if no attributes.
2276    fn ATTRIB(x: SEXP) -> SEXP;
2277
2278    /// Set the attributes pairlist of a SEXP.
2279    ///
2280    /// # Safety
2281    ///
2282    /// `v` must be a pairlist or R_NilValue
2283    fn SET_ATTRIB(x: SEXP, v: SEXP);
2284
2285    /// Check if SEXP has the "object" bit set (has a class).
2286    ///
2287    /// Returns non-zero if object has a class attribute.
2288    fn OBJECT(x: SEXP) -> ::std::os::raw::c_int;
2289
2290    /// Set the "object" bit.
2291    fn SET_OBJECT(x: SEXP, v: ::std::os::raw::c_int);
2292
2293    /// Get the LEVELS field (for factors).
2294    fn LEVELS(x: SEXP) -> ::std::os::raw::c_int;
2295
2296    /// Set the LEVELS field (for factors).
2297    ///
2298    /// Returns the value that was set.
2299    fn SETLEVELS(x: SEXP, v: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
2300
2301    // endregion
2302
2303    // region: ALTREP support
2304
2305    pub(crate) fn ALTREP_CLASS(x: SEXP) -> SEXP;
2306    pub(crate) fn R_altrep_data1(x: SEXP) -> SEXP;
2307    pub(crate) fn R_altrep_data2(x: SEXP) -> SEXP;
2308    pub(crate) fn R_set_altrep_data1(x: SEXP, v: SEXP);
2309    pub(crate) fn R_set_altrep_data2(x: SEXP, v: SEXP);
2310
2311    /// Check if a SEXP is an ALTREP object (returns non-zero if true).
2312    ///
2313    /// Use `SexpExt::is_altrep()` instead of calling this directly.
2314    fn ALTREP(x: SEXP) -> ::std::os::raw::c_int;
2315
2316    // endregion
2317
2318    // region: Vector data accessors (mutable pointers)
2319
2320    /// Get mutable pointer to logical vector data.
2321    ///
2322    /// For ALTREP vectors, this may force materialization.
2323    /// Get mutable pointer to logical vector data.
2324    ///
2325    /// For ALTREP vectors, this may force materialization.
2326    /// Prefer `SexpExt::set_logical_elt()` / `SexpExt::logical_elt()`.
2327    pub(crate) fn LOGICAL(x: SEXP) -> *mut ::std::os::raw::c_int;
2328
2329    /// Get mutable pointer to integer vector data.
2330    ///
2331    /// For ALTREP vectors, this may force materialization.
2332    /// Prefer `SexpExt::set_integer_elt()` / `SexpExt::integer_elt()`.
2333    pub(crate) fn INTEGER(x: SEXP) -> *mut ::std::os::raw::c_int;
2334
2335    /// Get mutable pointer to real vector data.
2336    ///
2337    /// For ALTREP vectors, this may force materialization.
2338    /// Prefer `SexpExt::set_real_elt()` / `SexpExt::real_elt()`.
2339    pub(crate) fn REAL(x: SEXP) -> *mut f64;
2340
2341    /// Get mutable pointer to complex vector data.
2342    ///
2343    /// For ALTREP vectors, this may force materialization.
2344    /// Prefer `SexpExt::set_complex_elt()` / `SexpExt::complex_elt()`.
2345    pub(crate) fn COMPLEX(x: SEXP) -> *mut Rcomplex;
2346
2347    /// Get mutable pointer to raw vector data.
2348    ///
2349    /// For ALTREP vectors, this may force materialization.
2350    /// Prefer `SexpExt::set_raw_elt()` / `SexpExt::raw_elt()`.
2351    pub(crate) fn RAW(x: SEXP) -> *mut Rbyte;
2352
2353    // endregion
2354
2355    // region: User interrupt and utilities
2356
2357    // utils.h
2358    pub fn R_CheckUserInterrupt();
2359
2360    // endregion
2361
2362    // region: Type checking — encapsulated by SexpExt::type_of()
2363
2364    fn TYPEOF(x: SEXP) -> SEXPTYPE;
2365
2366    // endregion
2367
2368    // Symbol creation and access
2369    #[doc(alias = "install")]
2370    pub fn Rf_install(name: *const ::std::os::raw::c_char) -> SEXP;
2371    /// Get the print name (CHARSXP) of a symbol (SYMSXP)
2372    fn PRINTNAME(x: SEXP) -> SEXP;
2373    /// Get the C string pointer from a CHARSXP — encapsulated by SexpExt::r_char()
2374    #[doc(alias = "CHAR")]
2375    fn R_CHAR(x: SEXP) -> *const ::std::os::raw::c_char;
2376
2377    // Attribute access
2378    // Attribute accessors — encapsulated by SexpExt methods
2379    /// Read an attribute from an object by symbol (e.g. `R_NamesSymbol`).
2380    ///
2381    /// Returns `R_NilValue` if the attribute is not set.
2382    #[doc(alias = "getAttrib")]
2383    fn Rf_getAttrib(vec: SEXP, name: SEXP) -> SEXP;
2384    /// Set the `names` attribute; returns the updated object.
2385    #[doc(alias = "namesgets")]
2386    fn Rf_namesgets(vec: SEXP, val: SEXP) -> SEXP;
2387    /// Set the `dim` attribute; returns the updated object.
2388    #[doc(alias = "dimgets")]
2389    fn Rf_dimgets(vec: SEXP, val: SEXP) -> SEXP;
2390
2391    // Duplication
2392    #[doc(alias = "duplicate")]
2393    fn Rf_duplicate(s: SEXP) -> SEXP;
2394    #[doc(alias = "shallow_duplicate")]
2395    fn Rf_shallow_duplicate(s: SEXP) -> SEXP;
2396
2397    // Object comparison
2398    /// Check if two R objects are identical (deep semantic equality).
2399    ///
2400    /// This is the C implementation of R's `identical()` function.
2401    ///
2402    /// # Flags
2403    ///
2404    /// Use the `IDENT_*` constants below. Flags are inverted: set bit = disable that check.
2405    ///
2406    /// **Default from R**: `IDENT_USE_CLOENV` (16) - ignore closure environments
2407    ///
2408    /// # Returns
2409    ///
2410    /// `TRUE` if identical, `FALSE` otherwise.
2411    ///
2412    /// # Performance
2413    ///
2414    /// Fast-path: Returns `TRUE` immediately if pointers are equal.
2415    pub fn R_compute_identical(x: SEXP, y: SEXP, flags: ::std::os::raw::c_int) -> Rboolean;
2416}
2417
2418/// Flags for `R_compute_identical` (bitmask, inverted logic: set bit = disable check).
2419pub const IDENT_NUM_AS_BITS: ::std::os::raw::c_int = 1;
2420/// Treat all NAs as identical (ignore NA payload differences).
2421pub const IDENT_NA_AS_BITS: ::std::os::raw::c_int = 2;
2422/// Compare attributes in order (not as a set).
2423pub const IDENT_ATTR_BY_ORDER: ::std::os::raw::c_int = 4;
2424/// Include bytecode in comparison.
2425pub const IDENT_USE_BYTECODE: ::std::os::raw::c_int = 8;
2426/// Include closure environments in comparison.
2427pub const IDENT_USE_CLOENV: ::std::os::raw::c_int = 16;
2428/// Include source references in comparison.
2429pub const IDENT_USE_SRCREF: ::std::os::raw::c_int = 32;
2430/// Compare external pointers as references (not by address).
2431pub const IDENT_EXTPTR_AS_REF: ::std::os::raw::c_int = 64;
2432
2433// Additional checked R API declarations used by conversion and reflection code.
2434#[allow(missing_docs)]
2435#[r_ffi_checked]
2436unsafe extern "C-unwind" {
2437    // Type coercion — encapsulated by SexpExt methods
2438    #[doc(alias = "asLogical")]
2439    fn Rf_asLogical(x: SEXP) -> ::std::os::raw::c_int;
2440    #[doc(alias = "asInteger")]
2441    fn Rf_asInteger(x: SEXP) -> ::std::os::raw::c_int;
2442    #[doc(alias = "asReal")]
2443    fn Rf_asReal(x: SEXP) -> f64;
2444    #[doc(alias = "asChar")]
2445    fn Rf_asChar(x: SEXP) -> SEXP;
2446    #[doc(alias = "coerceVector")]
2447    fn Rf_coerceVector(v: SEXP, sexptype: SEXPTYPE) -> SEXP;
2448
2449    // Matrix utilities — no callers outside ffi.rs
2450    #[doc(alias = "nrows")]
2451    fn Rf_nrows(x: SEXP) -> ::std::os::raw::c_int;
2452    #[doc(alias = "ncols")]
2453    fn Rf_ncols(x: SEXP) -> ::std::os::raw::c_int;
2454
2455    // Inheritance checking — encapsulated by SexpExt::inherits_class()
2456    #[doc(alias = "inherits")]
2457    fn Rf_inherits(x: SEXP, klass: *const ::std::os::raw::c_char) -> Rboolean;
2458
2459    // Type checking predicates — encapsulated by SexpExt type-check methods
2460    #[doc(alias = "isNull")]
2461    fn Rf_isNull(s: SEXP) -> Rboolean;
2462    #[doc(alias = "isSymbol")]
2463    fn Rf_isSymbol(s: SEXP) -> Rboolean;
2464    #[doc(alias = "isLogical")]
2465    fn Rf_isLogical(s: SEXP) -> Rboolean;
2466    #[doc(alias = "isReal")]
2467    fn Rf_isReal(s: SEXP) -> Rboolean;
2468    #[doc(alias = "isComplex")]
2469    fn Rf_isComplex(s: SEXP) -> Rboolean;
2470    #[doc(alias = "isExpression")]
2471    fn Rf_isExpression(s: SEXP) -> Rboolean;
2472    #[doc(alias = "isEnvironment")]
2473    fn Rf_isEnvironment(s: SEXP) -> Rboolean;
2474    #[doc(alias = "isString")]
2475    fn Rf_isString(s: SEXP) -> Rboolean;
2476
2477    // Composite type checking (from inline functions)
2478    #[doc(alias = "isArray")]
2479    fn Rf_isArray(s: SEXP) -> Rboolean;
2480    #[doc(alias = "isMatrix")]
2481    fn Rf_isMatrix(s: SEXP) -> Rboolean;
2482    #[doc(alias = "isList")]
2483    fn Rf_isList(s: SEXP) -> Rboolean;
2484    #[doc(alias = "isNewList")]
2485    fn Rf_isNewList(s: SEXP) -> Rboolean;
2486    #[doc(alias = "isPairList")]
2487    fn Rf_isPairList(s: SEXP) -> Rboolean;
2488    #[doc(alias = "isFunction")]
2489    fn Rf_isFunction(s: SEXP) -> Rboolean;
2490    #[doc(alias = "isPrimitive")]
2491    fn Rf_isPrimitive(s: SEXP) -> Rboolean;
2492    #[doc(alias = "isLanguage")]
2493    fn Rf_isLanguage(s: SEXP) -> Rboolean;
2494    #[doc(alias = "isDataFrame")]
2495    fn Rf_isDataFrame(s: SEXP) -> Rboolean;
2496    #[doc(alias = "isFactor")]
2497    fn Rf_isFactor(s: SEXP) -> Rboolean;
2498    #[doc(alias = "isInteger")]
2499    fn Rf_isInteger(s: SEXP) -> Rboolean;
2500    #[doc(alias = "isObject")]
2501    fn Rf_isObject(s: SEXP) -> Rboolean;
2502
2503    // Pairlist utilities
2504    #[doc(alias = "elt")]
2505    fn Rf_elt(list: SEXP, i: ::std::os::raw::c_int) -> SEXP;
2506    #[doc(alias = "lastElt")]
2507    fn Rf_lastElt(list: SEXP) -> SEXP;
2508    #[doc(alias = "nthcdr")]
2509    fn Rf_nthcdr(list: SEXP, n: ::std::os::raw::c_int) -> SEXP;
2510    #[doc(alias = "listAppend")]
2511    fn Rf_listAppend(s: SEXP, t: SEXP) -> SEXP;
2512
2513    // More attribute setters (using R's "gets" suffix convention)
2514    //
2515    // See "Attribute access" section above for explanation of the "gets" suffix.
2516    // These are setter functions equivalent to R's `attr(x) <- value` syntax.
2517
2518    /// Set the class attribute of a vector.
2519    ///
2520    /// Equivalent to R's `class(vec) <- klass` syntax.
2521    /// The "gets" suffix indicates this is a setter function.
2522    ///
2523    /// # Returns
2524    ///
2525    /// Returns the modified vector (like all "*gets" functions).
2526    #[doc(alias = "classgets")]
2527    fn Rf_classgets(vec: SEXP, klass: SEXP) -> SEXP;
2528
2529    /// Set the dimnames attribute of an array/matrix.
2530    ///
2531    /// Equivalent to R's `dimnames(vec) <- val` syntax.
2532    /// The "gets" suffix indicates this is a setter function.
2533    ///
2534    /// # Returns
2535    ///
2536    /// Returns the modified vector.
2537    #[doc(alias = "dimnamesgets")]
2538    fn Rf_dimnamesgets(vec: SEXP, val: SEXP) -> SEXP;
2539    #[doc(alias = "GetRowNames")]
2540    pub(crate) fn Rf_GetRowNames(dimnames: SEXP) -> SEXP;
2541    #[doc(alias = "GetColNames")]
2542    pub(crate) fn Rf_GetColNames(dimnames: SEXP) -> SEXP;
2543
2544    // Environment operations
2545    #[doc(alias = "findVar")]
2546    fn Rf_findVar(symbol: SEXP, rho: SEXP) -> SEXP;
2547    #[doc(alias = "findVarInFrame")]
2548    fn Rf_findVarInFrame(rho: SEXP, symbol: SEXP) -> SEXP;
2549    #[doc(alias = "findVarInFrame3")]
2550    fn Rf_findVarInFrame3(rho: SEXP, symbol: SEXP, doget: Rboolean) -> SEXP;
2551    #[doc(alias = "defineVar")]
2552    fn Rf_defineVar(symbol: SEXP, value: SEXP, rho: SEXP);
2553    #[doc(alias = "setVar")]
2554    fn Rf_setVar(symbol: SEXP, value: SEXP, rho: SEXP);
2555    #[doc(alias = "findFun")]
2556    fn Rf_findFun(symbol: SEXP, rho: SEXP) -> SEXP;
2557
2558    /// Find a registered namespace by name. **Longjmps on error** — prefer
2559    /// `REnv::package_namespace()` which wraps this safely.
2560    #[doc(alias = "FindNamespace")]
2561    fn R_FindNamespace(info: SEXP) -> SEXP;
2562
2563    /// Return the current execution environment (innermost closure on call
2564    /// stack, or `R_GlobalEnv` if none).
2565    #[doc(alias = "GetCurrentEnv")]
2566    pub(crate) fn R_GetCurrentEnv() -> SEXP;
2567
2568    // Evaluation
2569    #[doc(alias = "eval")]
2570    pub fn Rf_eval(expr: SEXP, rho: SEXP) -> SEXP;
2571    #[doc(alias = "applyClosure")]
2572    fn Rf_applyClosure(
2573        call: SEXP,
2574        op: SEXP,
2575        args: SEXP,
2576        rho: SEXP,
2577        suppliedvars: SEXP,
2578        check: Rboolean,
2579    ) -> SEXP;
2580    fn R_tryEval(expr: SEXP, env: SEXP, error_occurred: *mut ::std::os::raw::c_int) -> SEXP;
2581    pub(crate) fn R_tryEvalSilent(
2582        expr: SEXP,
2583        env: SEXP,
2584        error_occurred: *mut ::std::os::raw::c_int,
2585    ) -> SEXP;
2586    fn R_forceAndCall(e: SEXP, n: ::std::os::raw::c_int, rho: SEXP) -> SEXP;
2587}
2588
2589// region: Connections API (R_ext/Connections.h)
2590//
2591// Gated behind `connections` feature because R's connection API is explicitly UNSTABLE.
2592// From R_ext/Connections.h:
2593//   "IMPORTANT: we do not expect future connection APIs to be
2594//    backward-compatible so if you use this, you *must* check the
2595//    version and proceeds only if it matches what you expect.
2596//
2597//    We explicitly reserve the right to change the connection
2598//    implementation without a compatibility layer."
2599//
2600// Use with caution and always check R_CONNECTIONS_VERSION.
2601#[r_ffi_checked]
2602#[cfg(feature = "connections")]
2603unsafe extern "C-unwind" {
2604    /// Create a new custom connection.
2605    ///
2606    /// # WARNING
2607    ///
2608    /// This API is UNSTABLE. Check `R_CONNECTIONS_VERSION` before use.
2609    /// The connection implementation may change without notice.
2610    ///
2611    /// # Safety
2612    ///
2613    /// - `description`, `mode`, and `class_name` must be valid C strings
2614    /// - `ptr` must be a valid pointer to store the connection handle
2615    pub(crate) fn R_new_custom_connection(
2616        description: *const ::std::os::raw::c_char,
2617        mode: *const ::std::os::raw::c_char,
2618        class_name: *const ::std::os::raw::c_char,
2619        ptr: *mut Rconnection,
2620    ) -> SEXP;
2621
2622    /// Read from a connection.
2623    ///
2624    /// # WARNING
2625    ///
2626    /// This API is UNSTABLE and may change.
2627    ///
2628    /// # Safety
2629    ///
2630    /// - `con` must be a valid Rconnection handle
2631    /// - `buf` must be a valid buffer with at least `n` bytes
2632    pub(crate) fn R_ReadConnection(
2633        con: Rconnection,
2634        buf: *mut ::std::os::raw::c_void,
2635        n: usize,
2636    ) -> usize;
2637
2638    /// Write to a connection.
2639    ///
2640    /// # WARNING
2641    ///
2642    /// This API is UNSTABLE and may change.
2643    ///
2644    /// # Safety
2645    ///
2646    /// - `con` must be a valid Rconnection handle
2647    /// - `buf` must contain at least `n` valid bytes
2648    pub(crate) fn R_WriteConnection(
2649        con: Rconnection,
2650        buf: *const ::std::os::raw::c_void,
2651        n: usize,
2652    ) -> usize;
2653
2654    /// Get a connection from a SEXP.
2655    ///
2656    /// # WARNING
2657    ///
2658    /// This API is UNSTABLE and may change.
2659    /// Added in R 3.3.0.
2660    ///
2661    /// # Safety
2662    ///
2663    /// - `sConn` must be a valid connection SEXP
2664    pub(crate) fn R_GetConnection(sConn: SEXP) -> Rconnection;
2665}
2666// endregion: Connections API
2667
2668/// Check if a SEXP is an S4 object.
2669///
2670/// # Safety
2671///
2672/// - `arg1` must be a valid SEXP
2673#[allow(non_snake_case)]
2674unsafe fn Rf_isS4(arg1: SEXP) -> Rboolean {
2675    unsafe extern "C-unwind" {
2676        #[link_name = "Rf_isS4"]
2677        fn Rf_isS4_original(arg1: SEXP) -> u32;
2678    }
2679
2680    unsafe {
2681        if Rf_isS4_original(arg1) == 0 {
2682            Rboolean::FALSE
2683        } else {
2684            Rboolean::TRUE
2685        }
2686    }
2687}
2688
2689// region: registration!
2690
2691#[repr(C)]
2692#[derive(Debug)]
2693/// Opaque dynamic library descriptor from R.
2694pub struct DllInfo(::std::os::raw::c_void);
2695
2696/// Generic dynamic library function pointer.
2697///
2698/// R defines this as `void *(*)(void)` - a function taking no arguments and
2699/// returning `void*`. This is used for method registration and external pointer
2700/// functions. The actual function signatures vary; callers cast to the appropriate
2701/// concrete function type before calling.
2702///
2703/// We use `fn() -> *mut c_void` to match R's signature. The function pointer is
2704/// stored generically and cast to the appropriate type when called by R.
2705#[allow(non_camel_case_types)]
2706pub type DL_FUNC =
2707    ::std::option::Option<unsafe extern "C-unwind" fn() -> *mut ::std::os::raw::c_void>;
2708
2709/// Type descriptor for native primitive arguments in .C/.Fortran calls.
2710///
2711/// This is used in `R_CMethodDef` and `R_FortranMethodDef` to specify
2712/// argument types for type checking.
2713#[allow(non_camel_case_types)]
2714pub type R_NativePrimitiveArgType = ::std::os::raw::c_uint;
2715
2716/// Method definition for .C interface routines.
2717///
2718/// Used to register C functions callable via `.C()` from R.
2719#[repr(C)]
2720#[derive(Debug, Copy, Clone)]
2721#[allow(non_camel_case_types)]
2722#[allow(non_snake_case)]
2723pub struct R_CMethodDef {
2724    /// Exported symbol name.
2725    pub name: *const ::std::os::raw::c_char,
2726    /// Function pointer implementing the routine.
2727    pub fun: DL_FUNC,
2728    /// Declared arity.
2729    pub numArgs: ::std::os::raw::c_int,
2730    /// Optional array of argument types for type checking. May be null.
2731    pub types: *const R_NativePrimitiveArgType,
2732}
2733
2734/// Method definition for .Fortran interface routines.
2735///
2736/// Structurally identical to `R_CMethodDef`.
2737#[allow(non_camel_case_types)]
2738pub type R_FortranMethodDef = R_CMethodDef;
2739
2740/// Method definition for .Call interface routines.
2741///
2742/// Used to register C functions callable via `.Call()` from R.
2743/// Unlike `.C()` routines, `.Call()` functions receive and return SEXP values directly.
2744#[repr(C)]
2745#[derive(Debug, Copy, Clone)]
2746#[allow(non_camel_case_types)]
2747#[allow(non_snake_case)]
2748pub struct R_CallMethodDef {
2749    /// Exported symbol name.
2750    pub name: *const ::std::os::raw::c_char,
2751    /// Function pointer implementing the routine.
2752    pub fun: DL_FUNC,
2753    /// Declared arity.
2754    pub numArgs: ::std::os::raw::c_int,
2755}
2756
2757// SAFETY: `name` points to a static CStr literal, `fun` is a function pointer.
2758// Both are valid for program lifetime and safe to read from any thread.
2759unsafe impl Sync for R_CallMethodDef {}
2760unsafe impl Send for R_CallMethodDef {}
2761
2762/// Method definition for .External interface routines.
2763///
2764/// Structurally identical to `R_CallMethodDef`.
2765#[allow(non_camel_case_types)]
2766pub type R_ExternalMethodDef = R_CallMethodDef;
2767
2768// Checked routine registration API declarations.
2769#[allow(missing_docs)]
2770#[r_ffi_checked]
2771#[allow(clashing_extern_declarations)]
2772unsafe extern "C-unwind" {
2773    pub(crate) fn R_registerRoutines(
2774        info: *mut DllInfo,
2775        croutines: *const R_CMethodDef,
2776        callRoutines: *const R_CallMethodDef,
2777        fortranRoutines: *const R_FortranMethodDef,
2778        externalRoutines: *const R_ExternalMethodDef,
2779    ) -> ::std::os::raw::c_int;
2780
2781    pub(crate) fn R_useDynamicSymbols(info: *mut DllInfo, value: Rboolean) -> Rboolean;
2782    pub(crate) fn R_forceSymbols(info: *mut DllInfo, value: Rboolean) -> Rboolean;
2783}
2784
2785// endregion
2786
2787// region: Legacy `extern "C"` types (kept for compatibility testing)
2788
2789/// Legacy types using `extern "C"` ABI instead of `extern "C-unwind"`.
2790///
2791/// These are kept for compatibility testing. The main codebase uses
2792/// `extern "C-unwind"` everywhere to properly propagate Rust panics.
2793#[allow(clashing_extern_declarations)]
2794pub mod legacy_c {
2795    use super::{Rboolean, SEXP, r_ffi_checked};
2796
2797    #[allow(non_camel_case_types)]
2798    /// Legacy C finalizer callback type.
2799    pub type R_CFinalizer_t_C = ::std::option::Option<unsafe extern "C" fn(s: SEXP)>;
2800
2801    #[allow(non_camel_case_types)]
2802    /// Legacy generic function pointer type.
2803    pub type DL_FUNC_C =
2804        ::std::option::Option<unsafe extern "C" fn() -> *mut ::std::os::raw::c_void>;
2805
2806    #[repr(C)]
2807    #[derive(Debug, Copy, Clone)]
2808    #[allow(non_camel_case_types)]
2809    #[allow(non_snake_case)]
2810    /// Legacy `.Call` registration descriptor using `extern "C"` ABI.
2811    pub struct R_CallMethodDef_C {
2812        /// Exported symbol name.
2813        pub name: *const ::std::os::raw::c_char,
2814        /// Function pointer implementing the routine.
2815        pub fun: DL_FUNC_C,
2816        /// Declared arity.
2817        pub numArgs: ::std::os::raw::c_int,
2818    }
2819
2820    // Legacy `extern "C"` registration/finalizer declarations.
2821    #[allow(missing_docs, dead_code)]
2822    #[r_ffi_checked]
2823    unsafe extern "C" {
2824        /// Register a C finalizer callback.
2825        #[link_name = "R_RegisterCFinalizer"]
2826        fn R_RegisterCFinalizer_C(s: SEXP, fun: R_CFinalizer_t_C);
2827
2828        /// Register a C finalizer callback with `onexit` behavior.
2829        #[link_name = "R_RegisterCFinalizerEx"]
2830        fn R_RegisterCFinalizerEx_C(s: SEXP, fun: R_CFinalizer_t_C, onexit: Rboolean);
2831
2832        /// Create function external pointer (`R_MakeExternalPtrFn`).
2833        #[link_name = "R_MakeExternalPtrFn"]
2834        fn R_MakeExternalPtrFn_C(p: DL_FUNC_C, tag: SEXP, prot: SEXP) -> SEXP;
2835
2836        /// Extract function pointer from external pointer.
2837        #[link_name = "R_ExternalPtrAddrFn"]
2838        fn R_ExternalPtrAddrFn_C(s: SEXP) -> DL_FUNC_C;
2839
2840        /// Register native routines using legacy ABI types.
2841        #[link_name = "R_registerRoutines"]
2842        fn R_registerRoutines_C(
2843            info: *mut super::DllInfo,
2844            croutines: *const ::std::os::raw::c_void,
2845            callRoutines: *const R_CallMethodDef_C,
2846            fortranRoutines: *const ::std::os::raw::c_void,
2847            externalRoutines: *const ::std::os::raw::c_void,
2848        ) -> ::std::os::raw::c_int;
2849    }
2850}
2851
2852// endregion
2853
2854// region: Non-API encoding/locale state (Defn.h)
2855
2856/// Non-API encoding / locale helpers from R's `Defn.h`.
2857///
2858/// These are not part of the stable R API and may break across R versions.
2859#[cfg(feature = "nonapi")]
2860pub mod nonapi_encoding {
2861    use super::r_ffi_checked;
2862
2863    #[r_ffi_checked]
2864    #[allow(clashing_extern_declarations)]
2865    unsafe extern "C-unwind" {
2866        pub(crate) fn R_nativeEncoding() -> *const ::std::os::raw::c_char;
2867
2868        // Locale flags
2869        pub(crate) static utf8locale: super::Rboolean;
2870        pub(crate) static latin1locale: super::Rboolean;
2871
2872        // Set when R "knows" it is running in UTF-8.
2873        pub(crate) static known_to_be_utf8: super::Rboolean;
2874    }
2875}
2876
2877// endregion
2878
2879// region: Non-API stack checking variables (Rinterface.h)
2880
2881/// Non-API stack checking variables from `Rinterface.h`.
2882///
2883/// R uses these to detect stack overflow. When calling R from a thread other
2884/// than the main R thread, stack checking will fail because these values are
2885/// set for the main thread's stack.
2886///
2887/// # Usage
2888///
2889/// To safely call R from a worker thread, disable stack checking:
2890/// ```ignore
2891/// #[cfg(feature = "nonapi")]
2892/// unsafe {
2893///     use miniextendr_api::ffi::nonapi_stack::*;
2894///     let saved = get_r_cstack_limit();
2895///     set_r_cstack_limit(usize::MAX); // disable checking
2896///     // ... call R APIs ...
2897///     set_r_cstack_limit(saved); // restore
2898/// }
2899/// ```
2900///
2901/// Or use the higher-level [`StackCheckGuard`](crate::thread::StackCheckGuard) which handles this automatically.
2902///
2903/// Setting `R_CStackLimit` to `usize::MAX` (i.e., `-1` as `uintptr_t`) disables
2904/// stack checking entirely.
2905#[cfg(feature = "nonapi")]
2906pub mod nonapi_stack {
2907    unsafe extern "C" {
2908        /// Top of the stack (set during `Rf_initialize_R` for main thread).
2909        ///
2910        /// On Unix, determined via `__libc_stack_end`, `KERN_USRSTACK`, or
2911        /// `thr_stksegment`. On Windows, via `VirtualQuery`.
2912        #[allow(non_upper_case_globals)]
2913        pub(crate) static R_CStackStart: usize;
2914
2915        /// Stack size limit. Set to `usize::MAX` to disable stack checking.
2916        ///
2917        /// From R source: `if(R_CStackStart == -1) R_CStackLimit = -1; /* never set */`
2918        #[allow(non_upper_case_globals)]
2919        pub static R_CStackLimit: usize;
2920
2921        /// Stack growth direction: 1 = grows up, -1 = grows down.
2922        ///
2923        /// Most systems (x86, ARM) grow down (-1).
2924        #[allow(non_upper_case_globals)]
2925        pub(crate) static R_CStackDir: ::std::os::raw::c_int;
2926    }
2927
2928    /// Write to `R_CStackLimit`.
2929    ///
2930    /// # Safety
2931    /// Must be called from R's main thread.
2932    #[inline]
2933    pub unsafe fn set_r_cstack_limit(value: usize) {
2934        unsafe {
2935            let ptr = (&raw const R_CStackLimit).cast_mut();
2936            ptr.write(value);
2937        }
2938    }
2939
2940    /// Read `R_CStackLimit`.
2941    #[inline]
2942    pub(crate) fn get_r_cstack_limit() -> usize {
2943        unsafe { R_CStackLimit }
2944    }
2945
2946    /// Read `R_CStackStart`.
2947    #[inline]
2948    pub(crate) fn get_r_cstack_start() -> usize {
2949        unsafe { R_CStackStart }
2950    }
2951
2952    /// Read `R_CStackDir`.
2953    #[inline]
2954    pub(crate) fn get_r_cstack_dir() -> ::std::os::raw::c_int {
2955        unsafe { R_CStackDir }
2956    }
2957}
2958
2959// endregion
2960
2961// region: Inline Helper Functions (Rust implementations of R's inline functions)
2962
2963/// Create a length-1 string vector from a C string.
2964///
2965/// Rust equivalent of R's inline `Rf_mkString(s)`, which is
2966/// shorthand for `ScalarString(mkChar(s))`.
2967///
2968/// # Safety
2969///
2970/// - `s` must be a valid null-terminated C string
2971/// - Must be called from R's main thread
2972/// - Result must be protected from GC
2973#[doc(alias = "mkString")]
2974#[allow(non_snake_case)]
2975#[inline]
2976pub unsafe fn Rf_mkString(s: *const ::std::os::raw::c_char) -> SEXP {
2977    unsafe {
2978        let charsxp = Rf_mkChar(s);
2979        let protected = Rf_protect(charsxp);
2980        let result = Rf_ScalarString(protected);
2981        Rf_unprotect(1);
2982        result
2983    }
2984}
2985
2986/// Build a pairlist with 1 element.
2987///
2988/// Rust equivalent of R's inline `Rf_list1(s)`.
2989///
2990/// # Safety
2991///
2992/// - `s` must be a valid SEXP
2993/// - Must be called from R's main thread
2994/// - Result must be protected from GC
2995#[doc(alias = "list1")]
2996#[allow(non_snake_case)]
2997#[inline]
2998pub unsafe fn Rf_list1(s: SEXP) -> SEXP {
2999    unsafe { Rf_cons(s, R_NilValue) }
3000}
3001
3002/// Build a pairlist with 2 elements.
3003///
3004/// Rust equivalent of R's inline `Rf_list2(s, t)`.
3005///
3006/// # Safety
3007///
3008/// - Both SEXPs must be valid
3009/// - Must be called from R's main thread
3010/// - Result must be protected from GC
3011#[doc(alias = "list2")]
3012#[allow(non_snake_case)]
3013#[inline]
3014pub unsafe fn Rf_list2(s: SEXP, t: SEXP) -> SEXP {
3015    unsafe { Rf_cons(s, Rf_cons(t, R_NilValue)) }
3016}
3017
3018/// Build a pairlist with 3 elements.
3019///
3020/// Rust equivalent of R's inline `Rf_list3(s, t, u)`.
3021///
3022/// # Safety
3023///
3024/// - All SEXPs must be valid
3025/// - Must be called from R's main thread
3026/// - Result must be protected from GC
3027#[doc(alias = "list3")]
3028#[allow(non_snake_case)]
3029#[inline]
3030pub unsafe fn Rf_list3(s: SEXP, t: SEXP, u: SEXP) -> SEXP {
3031    unsafe { Rf_cons(s, Rf_cons(t, Rf_cons(u, R_NilValue))) }
3032}
3033
3034/// Build a pairlist with 4 elements.
3035///
3036/// Rust equivalent of R's inline `Rf_list4(s, t, u, v)`.
3037///
3038/// # Safety
3039///
3040/// - All SEXPs must be valid
3041/// - Must be called from R's main thread
3042/// - Result must be protected from GC
3043#[doc(alias = "list4")]
3044#[allow(non_snake_case)]
3045#[inline]
3046pub unsafe fn Rf_list4(s: SEXP, t: SEXP, u: SEXP, v: SEXP) -> SEXP {
3047    unsafe { Rf_cons(s, Rf_cons(t, Rf_cons(u, Rf_cons(v, R_NilValue)))) }
3048}
3049
3050/// Build a language object (call) with 1 element (the function).
3051///
3052/// Rust equivalent of R's inline `Rf_lang1(s)`.
3053/// Creates a call like `f()` where `s` is the function.
3054///
3055/// # Safety
3056///
3057/// - `s` must be a valid SEXP (typically a symbol or closure)
3058/// - Must be called from R's main thread
3059/// - Result must be protected from GC
3060#[doc(alias = "lang1")]
3061#[allow(non_snake_case)]
3062#[inline]
3063pub unsafe fn Rf_lang1(s: SEXP) -> SEXP {
3064    unsafe { Rf_lcons(s, R_NilValue) }
3065}
3066
3067/// Build a language object (call) with function and 1 argument.
3068///
3069/// Rust equivalent of R's inline `Rf_lang2(s, t)`.
3070/// Creates a call like `f(arg)` where `s` is the function and `t` is the argument.
3071///
3072/// # Safety
3073///
3074/// - Both SEXPs must be valid
3075/// - Must be called from R's main thread
3076/// - Result must be protected from GC
3077#[doc(alias = "lang2")]
3078#[allow(non_snake_case)]
3079#[inline]
3080pub unsafe fn Rf_lang2(s: SEXP, t: SEXP) -> SEXP {
3081    unsafe { Rf_lcons(s, Rf_list1(t)) }
3082}
3083
3084/// Build a language object (call) with function and 2 arguments.
3085///
3086/// Rust equivalent of R's inline `Rf_lang3(s, t, u)`.
3087/// Creates a call like `f(arg1, arg2)`.
3088///
3089/// # Safety
3090///
3091/// - All SEXPs must be valid
3092/// - Must be called from R's main thread
3093/// - Result must be protected from GC
3094#[doc(alias = "lang3")]
3095#[allow(non_snake_case)]
3096#[inline]
3097pub unsafe fn Rf_lang3(s: SEXP, t: SEXP, u: SEXP) -> SEXP {
3098    unsafe { Rf_lcons(s, Rf_list2(t, u)) }
3099}
3100
3101/// Build a language object (call) with function and 3 arguments.
3102///
3103/// Rust equivalent of R's inline `Rf_lang4(s, t, u, v)`.
3104/// Creates a call like `f(arg1, arg2, arg3)`.
3105///
3106/// # Safety
3107///
3108/// - All SEXPs must be valid
3109/// - Must be called from R's main thread
3110/// - Result must be protected from GC
3111#[doc(alias = "lang4")]
3112#[allow(non_snake_case)]
3113#[inline]
3114pub unsafe fn Rf_lang4(s: SEXP, t: SEXP, u: SEXP, v: SEXP) -> SEXP {
3115    unsafe { Rf_lcons(s, Rf_list3(t, u, v)) }
3116}
3117
3118/// Build a language object (call) with function and 4 arguments.
3119///
3120/// Rust equivalent of R's inline `Rf_lang5(s, t, u, v, w)`.
3121/// Creates a call like `f(arg1, arg2, arg3, arg4)`.
3122///
3123/// # Safety
3124///
3125/// - All SEXPs must be valid
3126/// - Must be called from R's main thread
3127/// - Result must be protected from GC
3128#[doc(alias = "lang5")]
3129#[allow(non_snake_case)]
3130#[inline]
3131pub unsafe fn Rf_lang5(s: SEXP, t: SEXP, u: SEXP, v: SEXP, w: SEXP) -> SEXP {
3132    unsafe { Rf_lcons(s, Rf_list4(t, u, v, w)) }
3133}
3134
3135/// Build a language object (call) with function and 5 arguments.
3136///
3137/// Rust equivalent of R's inline `Rf_lang6(s, t, u, v, w, x)`.
3138/// Creates a call like `f(arg1, arg2, arg3, arg4, arg5)`.
3139///
3140/// # Safety
3141///
3142/// - All SEXPs must be valid
3143/// - Must be called from R's main thread
3144/// - Result must be protected from GC
3145#[doc(alias = "lang6")]
3146#[allow(non_snake_case)]
3147#[inline]
3148pub unsafe fn Rf_lang6(s: SEXP, t: SEXP, u: SEXP, v: SEXP, w: SEXP, x: SEXP) -> SEXP {
3149    unsafe {
3150        let protected = Rf_protect(s);
3151        let list = Rf_cons(t, Rf_list4(u, v, w, x));
3152        let result = Rf_lcons(protected, list);
3153        Rf_unprotect(1);
3154        result
3155    }
3156}
3157
3158// endregion
3159
3160// region: RNG functions (R_ext/Random.h)
3161
3162/// RNG type enum from R_ext/Random.h
3163#[repr(u32)]
3164#[non_exhaustive]
3165#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
3166#[allow(non_camel_case_types)]
3167pub enum RNGtype {
3168    /// Wichmann-Hill generator.
3169    WICHMANN_HILL = 0,
3170    /// Marsaglia-Multicarry generator.
3171    MARSAGLIA_MULTICARRY = 1,
3172    /// Super-Duper generator.
3173    SUPER_DUPER = 2,
3174    /// Mersenne Twister generator.
3175    MERSENNE_TWISTER = 3,
3176    /// Knuth TAOCP generator.
3177    KNUTH_TAOCP = 4,
3178    /// User-supplied uniform generator.
3179    USER_UNIF = 5,
3180    /// Knuth TAOCP 2002 variant.
3181    KNUTH_TAOCP2 = 6,
3182    /// L'Ecuyer-CMRG generator.
3183    LECUYER_CMRG = 7,
3184}
3185
3186/// Normal distribution generator type enum from R_ext/Random.h
3187#[repr(u32)]
3188#[non_exhaustive]
3189#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
3190#[allow(non_camel_case_types)]
3191pub enum N01type {
3192    /// Legacy buggy Kinderman-Ramage method.
3193    BUGGY_KINDERMAN_RAMAGE = 0,
3194    /// Ahrens-Dieter method.
3195    AHRENS_DIETER = 1,
3196    /// Box-Muller transform.
3197    BOX_MULLER = 2,
3198    /// User-supplied normal generator.
3199    USER_NORM = 3,
3200    /// Inversion method.
3201    INVERSION = 4,
3202    /// Fixed Kinderman-Ramage method.
3203    KINDERMAN_RAMAGE = 5,
3204}
3205
3206/// Discrete uniform sample method enum from R_ext/Random.h
3207#[repr(u32)]
3208#[non_exhaustive]
3209#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
3210#[allow(non_camel_case_types)]
3211pub enum Sampletype {
3212    /// Rounding method for integer sampling.
3213    ROUNDING = 0,
3214    /// Rejection sampling method.
3215    REJECTION = 1,
3216}
3217
3218#[r_ffi_checked]
3219unsafe extern "C-unwind" {
3220    /// Save the current RNG state from R's global state.
3221    ///
3222    /// Must be called before using `unif_rand()`, `norm_rand()`, etc.
3223    /// The state is restored with `PutRNGstate()`.
3224    ///
3225    /// # Example
3226    ///
3227    /// ```ignore
3228    /// unsafe {
3229    ///     GetRNGstate();
3230    ///     let x = unif_rand();
3231    ///     let y = norm_rand();
3232    ///     PutRNGstate();
3233    /// }
3234    /// ```
3235    pub fn GetRNGstate();
3236
3237    /// Restore the RNG state to R's global state.
3238    ///
3239    /// Must be called after using `unif_rand()`, `norm_rand()`, etc.
3240    /// to ensure R's `.Random.seed` is updated.
3241    pub fn PutRNGstate();
3242
3243    /// Generate a uniform random number in (0, 1).
3244    ///
3245    /// # Important
3246    ///
3247    /// Must call `GetRNGstate()` before and `PutRNGstate()` after.
3248    pub fn unif_rand() -> f64;
3249
3250    /// Generate a standard normal random number (mean 0, sd 1).
3251    ///
3252    /// # Important
3253    ///
3254    /// Must call `GetRNGstate()` before and `PutRNGstate()` after.
3255    pub fn norm_rand() -> f64;
3256
3257    /// Generate an exponential random number with rate 1.
3258    ///
3259    /// # Important
3260    ///
3261    /// Must call `GetRNGstate()` before and `PutRNGstate()` after.
3262    pub fn exp_rand() -> f64;
3263
3264    /// Generate a uniform random index in [0, dn).
3265    ///
3266    /// Used for sampling without bias for large n.
3267    ///
3268    /// # Important
3269    ///
3270    /// Must call `GetRNGstate()` before and `PutRNGstate()` after.
3271    pub fn R_unif_index(dn: f64) -> f64;
3272
3273    /// Get the current discrete uniform sample method.
3274    fn R_sample_kind() -> Sampletype;
3275}
3276
3277// endregion
3278
3279// region: Memory allocation (R_ext/Memory.h)
3280
3281#[r_ffi_checked]
3282unsafe extern "C-unwind" {
3283    /// Get the current R memory stack watermark.
3284    ///
3285    /// Use with `vmaxset()` to restore memory stack state.
3286    /// Memory allocated with `R_alloc()` between `vmaxget()` and `vmaxset()`
3287    /// will be freed when `vmaxset()` is called.
3288    ///
3289    /// # Example
3290    ///
3291    /// ```ignore
3292    /// unsafe {
3293    ///     let watermark = vmaxget();
3294    ///     let buf = R_alloc(100, 1);
3295    ///     // ... use buf ...
3296    ///     vmaxset(watermark); // frees buf
3297    /// }
3298    /// ```
3299    fn vmaxget() -> *mut ::std::os::raw::c_void;
3300
3301    /// Set the R memory stack watermark, freeing memory allocated since the mark.
3302    ///
3303    /// # Safety
3304    ///
3305    /// `ovmax` must be a value returned by `vmaxget()` called earlier in the
3306    /// same R evaluation context.
3307    fn vmaxset(ovmax: *const ::std::os::raw::c_void);
3308
3309    /// Run the R garbage collector.
3310    ///
3311    /// Forces a full garbage collection cycle.
3312    pub fn R_gc();
3313
3314    /// Check if the garbage collector is currently running.
3315    ///
3316    /// Returns non-zero if GC is in progress.
3317    fn R_gc_running() -> ::std::os::raw::c_int;
3318
3319    /// Allocate memory on R's memory stack.
3320    ///
3321    /// This memory is automatically freed when the calling R function returns,
3322    /// or can be freed earlier with `vmaxset()`.
3323    ///
3324    /// # Parameters
3325    ///
3326    /// - `nelem`: Number of elements to allocate
3327    /// - `eltsize`: Size of each element in bytes
3328    ///
3329    /// # Returns
3330    ///
3331    /// Pointer to allocated memory (as `char*` for compatibility with S).
3332    fn R_alloc(nelem: usize, eltsize: ::std::os::raw::c_int) -> *mut ::std::os::raw::c_char;
3333
3334    /// Allocate an array of long doubles on R's memory stack.
3335    ///
3336    /// # Parameters
3337    ///
3338    /// - `nelem`: Number of long double elements to allocate
3339    fn R_allocLD(nelem: usize) -> *mut f64; // Note: f64 is close enough for most uses
3340
3341    /// S compatibility: allocate zeroed memory on R's memory stack.
3342    ///
3343    /// # Parameters
3344    ///
3345    /// - `nelem`: Number of elements
3346    /// - `eltsize`: Size of each element
3347    fn S_alloc(
3348        nelem: ::std::os::raw::c_long,
3349        eltsize: ::std::os::raw::c_int,
3350    ) -> *mut ::std::os::raw::c_char;
3351
3352    /// S compatibility: reallocate memory on R's memory stack.
3353    ///
3354    /// # Safety
3355    ///
3356    /// `ptr` must have been allocated by `S_alloc`.
3357    fn S_realloc(
3358        ptr: *mut ::std::os::raw::c_char,
3359        newsize: ::std::os::raw::c_long,
3360        oldsize: ::std::os::raw::c_long,
3361        eltsize: ::std::os::raw::c_int,
3362    ) -> *mut ::std::os::raw::c_char;
3363
3364    /// GC-aware malloc.
3365    ///
3366    /// Triggers GC if allocation fails, then retries.
3367    /// Memory must be freed with `free()`.
3368    fn R_malloc_gc(size: usize) -> *mut ::std::os::raw::c_void;
3369
3370    /// GC-aware calloc.
3371    ///
3372    /// Triggers GC if allocation fails, then retries.
3373    /// Memory must be freed with `free()`.
3374    fn R_calloc_gc(nelem: usize, eltsize: usize) -> *mut ::std::os::raw::c_void;
3375
3376    /// GC-aware realloc.
3377    ///
3378    /// Triggers GC if allocation fails, then retries.
3379    /// Memory must be freed with `free()`.
3380    fn R_realloc_gc(ptr: *mut ::std::os::raw::c_void, size: usize) -> *mut ::std::os::raw::c_void;
3381}
3382
3383// endregion
3384
3385// region: Sorting and utility functions (R_ext/Utils.h)
3386
3387#[r_ffi_checked]
3388unsafe extern "C-unwind" {
3389    /// Sort an integer vector in place (ascending order).
3390    ///
3391    /// # Parameters
3392    ///
3393    /// - `x`: Pointer to integer array
3394    /// - `n`: Number of elements
3395    fn R_isort(x: *mut ::std::os::raw::c_int, n: ::std::os::raw::c_int);
3396
3397    /// Sort a double vector in place (ascending order).
3398    ///
3399    /// # Parameters
3400    ///
3401    /// - `x`: Pointer to double array
3402    /// - `n`: Number of elements
3403    fn R_rsort(x: *mut f64, n: ::std::os::raw::c_int);
3404
3405    /// Sort a complex vector in place.
3406    ///
3407    /// # Parameters
3408    ///
3409    /// - `x`: Pointer to Rcomplex array
3410    /// - `n`: Number of elements
3411    fn R_csort(x: *mut Rcomplex, n: ::std::os::raw::c_int);
3412
3413    /// Sort doubles in descending order, carrying along an index array.
3414    ///
3415    /// # Parameters
3416    ///
3417    /// - `a`: Pointer to double array (sorted in place, descending)
3418    /// - `ib`: Pointer to integer array (permuted alongside `a`)
3419    /// - `n`: Number of elements
3420    #[doc(alias = "Rf_revsort")]
3421    fn revsort(a: *mut f64, ib: *mut ::std::os::raw::c_int, n: ::std::os::raw::c_int);
3422
3423    /// Sort doubles with index array.
3424    ///
3425    /// # Parameters
3426    ///
3427    /// - `x`: Pointer to double array (sorted in place)
3428    /// - `indx`: Pointer to integer array (permuted alongside `x`)
3429    /// - `n`: Number of elements
3430    fn rsort_with_index(x: *mut f64, indx: *mut ::std::os::raw::c_int, n: ::std::os::raw::c_int);
3431
3432    /// Partial sort integers (moves k-th smallest to position k).
3433    ///
3434    /// # Parameters
3435    ///
3436    /// - `x`: Pointer to integer array
3437    /// - `n`: Number of elements
3438    /// - `k`: Target position (0-indexed)
3439    #[doc(alias = "Rf_iPsort")]
3440    fn iPsort(x: *mut ::std::os::raw::c_int, n: ::std::os::raw::c_int, k: ::std::os::raw::c_int);
3441
3442    /// Partial sort doubles (moves k-th smallest to position k).
3443    ///
3444    /// # Parameters
3445    ///
3446    /// - `x`: Pointer to double array
3447    /// - `n`: Number of elements
3448    /// - `k`: Target position (0-indexed)
3449    #[doc(alias = "Rf_rPsort")]
3450    fn rPsort(x: *mut f64, n: ::std::os::raw::c_int, k: ::std::os::raw::c_int);
3451
3452    /// Partial sort complex numbers.
3453    ///
3454    /// # Parameters
3455    ///
3456    /// - `x`: Pointer to Rcomplex array
3457    /// - `n`: Number of elements
3458    /// - `k`: Target position (0-indexed)
3459    #[doc(alias = "Rf_cPsort")]
3460    fn cPsort(x: *mut Rcomplex, n: ::std::os::raw::c_int, k: ::std::os::raw::c_int);
3461
3462    /// Quicksort doubles in place.
3463    ///
3464    /// # Parameters
3465    ///
3466    /// - `v`: Pointer to double array
3467    /// - `i`: Start index (1-indexed for R compatibility)
3468    /// - `j`: End index (1-indexed)
3469    fn R_qsort(v: *mut f64, i: usize, j: usize);
3470
3471    /// Quicksort doubles with index array.
3472    ///
3473    /// # Parameters
3474    ///
3475    /// - `v`: Pointer to double array
3476    /// - `indx`: Pointer to index array (permuted alongside v)
3477    /// - `i`: Start index (1-indexed)
3478    /// - `j`: End index (1-indexed)
3479    fn R_qsort_I(
3480        v: *mut f64,
3481        indx: *mut ::std::os::raw::c_int,
3482        i: ::std::os::raw::c_int,
3483        j: ::std::os::raw::c_int,
3484    );
3485
3486    /// Quicksort integers in place.
3487    ///
3488    /// # Parameters
3489    ///
3490    /// - `iv`: Pointer to integer array
3491    /// - `i`: Start index (1-indexed)
3492    /// - `j`: End index (1-indexed)
3493    fn R_qsort_int(iv: *mut ::std::os::raw::c_int, i: usize, j: usize);
3494
3495    /// Quicksort integers with index array.
3496    ///
3497    /// # Parameters
3498    ///
3499    /// - `iv`: Pointer to integer array
3500    /// - `indx`: Pointer to index array
3501    /// - `i`: Start index (1-indexed)
3502    /// - `j`: End index (1-indexed)
3503    fn R_qsort_int_I(
3504        iv: *mut ::std::os::raw::c_int,
3505        indx: *mut ::std::os::raw::c_int,
3506        i: ::std::os::raw::c_int,
3507        j: ::std::os::raw::c_int,
3508    );
3509
3510    /// Expand a filename, resolving `~` and environment variables.
3511    ///
3512    /// # Returns
3513    ///
3514    /// Pointer to expanded path (in R's internal buffer, do not free).
3515    fn R_ExpandFileName(s: *const ::std::os::raw::c_char) -> *const ::std::os::raw::c_char;
3516
3517    /// Convert string to double, always using '.' as decimal point.
3518    ///
3519    /// Also accepts "NA" as input, returning NA_REAL.
3520    fn R_atof(str: *const ::std::os::raw::c_char) -> f64;
3521
3522    /// Convert string to double with end pointer, using '.' as decimal point.
3523    ///
3524    /// Like `strtod()` but locale-independent.
3525    fn R_strtod(c: *const ::std::os::raw::c_char, end: *mut *mut ::std::os::raw::c_char) -> f64;
3526
3527    /// Generate a temporary filename.
3528    ///
3529    /// # Parameters
3530    ///
3531    /// - `prefix`: Filename prefix
3532    /// - `tempdir`: Directory for temp file
3533    ///
3534    /// # Returns
3535    ///
3536    /// Newly allocated string (must be freed with `R_free_tmpnam`).
3537    fn R_tmpnam(
3538        prefix: *const ::std::os::raw::c_char,
3539        tempdir: *const ::std::os::raw::c_char,
3540    ) -> *mut ::std::os::raw::c_char;
3541
3542    /// Generate a temporary filename with extension.
3543    ///
3544    /// # Parameters
3545    ///
3546    /// - `prefix`: Filename prefix
3547    /// - `tempdir`: Directory for temp file
3548    /// - `fileext`: File extension (e.g., ".txt")
3549    ///
3550    /// # Returns
3551    ///
3552    /// Newly allocated string (must be freed with `R_free_tmpnam`).
3553    fn R_tmpnam2(
3554        prefix: *const ::std::os::raw::c_char,
3555        tempdir: *const ::std::os::raw::c_char,
3556        fileext: *const ::std::os::raw::c_char,
3557    ) -> *mut ::std::os::raw::c_char;
3558
3559    /// Free a temporary filename allocated by `R_tmpnam` or `R_tmpnam2`.
3560    fn R_free_tmpnam(name: *mut ::std::os::raw::c_char);
3561
3562    /// Check for R stack overflow.
3563    ///
3564    /// Throws an R error if stack is nearly exhausted.
3565    fn R_CheckStack();
3566
3567    /// Check for R stack overflow with extra space requirement.
3568    ///
3569    /// # Parameters
3570    ///
3571    /// - `extra`: Additional bytes needed
3572    fn R_CheckStack2(extra: usize);
3573
3574    /// Find the interval containing a value (binary search).
3575    ///
3576    /// Used for interpolation and binning.
3577    ///
3578    /// # Parameters
3579    ///
3580    /// - `xt`: Sorted breakpoints array
3581    /// - `n`: Number of breakpoints
3582    /// - `x`: Value to find
3583    /// - `rightmost_closed`: If TRUE, rightmost interval is closed
3584    /// - `all_inside`: If TRUE, out-of-bounds values map to endpoints
3585    /// - `ilo`: Initial guess for interval (1-indexed)
3586    /// - `mflag`: Output flag (see R documentation)
3587    ///
3588    /// # Returns
3589    ///
3590    /// Interval index (1-indexed).
3591    fn findInterval(
3592        xt: *const f64,
3593        n: ::std::os::raw::c_int,
3594        x: f64,
3595        rightmost_closed: Rboolean,
3596        all_inside: Rboolean,
3597        ilo: ::std::os::raw::c_int,
3598        mflag: *mut ::std::os::raw::c_int,
3599    ) -> ::std::os::raw::c_int;
3600
3601    /// Extended interval finding with left-open option.
3602    #[allow(clippy::too_many_arguments)]
3603    fn findInterval2(
3604        xt: *const f64,
3605        n: ::std::os::raw::c_int,
3606        x: f64,
3607        rightmost_closed: Rboolean,
3608        all_inside: Rboolean,
3609        left_open: Rboolean,
3610        ilo: ::std::os::raw::c_int,
3611        mflag: *mut ::std::os::raw::c_int,
3612    ) -> ::std::os::raw::c_int;
3613
3614    /// Find column maxima in a matrix.
3615    ///
3616    /// # Parameters
3617    ///
3618    /// - `matrix`: Column-major matrix data
3619    /// - `nr`: Number of rows
3620    /// - `nc`: Number of columns
3621    /// - `maxes`: Output array for column maxima indices (1-indexed)
3622    /// - `ties_meth`: How to handle ties (1=first, 2=random, 3=last)
3623    fn R_max_col(
3624        matrix: *const f64,
3625        nr: *const ::std::os::raw::c_int,
3626        nc: *const ::std::os::raw::c_int,
3627        maxes: *mut ::std::os::raw::c_int,
3628        ties_meth: *const ::std::os::raw::c_int,
3629    );
3630
3631    /// Check if a string represents FALSE in R.
3632    ///
3633    /// Recognizes "FALSE", "false", "False", "F", "f", etc.
3634    #[doc(alias = "Rf_StringFalse")]
3635    fn StringFalse(s: *const ::std::os::raw::c_char) -> Rboolean;
3636
3637    /// Check if a string represents TRUE in R.
3638    ///
3639    /// Recognizes "TRUE", "true", "True", "T", "t", etc.
3640    #[doc(alias = "Rf_StringTrue")]
3641    fn StringTrue(s: *const ::std::os::raw::c_char) -> Rboolean;
3642
3643    /// Check if a string is blank (empty or only whitespace).
3644    #[doc(alias = "Rf_isBlankString")]
3645    fn isBlankString(s: *const ::std::os::raw::c_char) -> Rboolean;
3646}
3647
3648// endregion
3649
3650// region: Additional Rinternals.h functions
3651
3652#[r_ffi_checked]
3653unsafe extern "C-unwind" {
3654    // String/character functions
3655
3656    /// Create a CHARSXP with specified encoding.
3657    ///
3658    /// # Parameters
3659    ///
3660    /// - `s`: C string
3661    /// - `encoding`: Character encoding (CE_UTF8, CE_LATIN1, etc.)
3662    #[doc(alias = "mkCharCE")]
3663    pub(crate) fn Rf_mkCharCE(s: *const ::std::os::raw::c_char, encoding: cetype_t) -> SEXP;
3664
3665    /// Get the number of characters in a string/character.
3666    ///
3667    /// # Parameters
3668    ///
3669    /// - `x`: A string SEXP
3670    /// - `ntype`: Type of count (0=bytes, 1=chars, 2=width)
3671    /// - `allowNA`: Whether to allow NA values
3672    /// - `keepNA`: Whether to keep NA in result
3673    /// - `msg_name`: Name for error messages
3674    ///
3675    /// # Returns
3676    ///
3677    /// Character count or -1 on error.
3678    fn R_nchar(
3679        x: SEXP,
3680        ntype: ::std::os::raw::c_int,
3681        allowNA: Rboolean,
3682        keepNA: Rboolean,
3683        msg_name: *const ::std::os::raw::c_char,
3684    ) -> ::std::os::raw::c_int;
3685
3686    /// Convert SEXPTYPE to C string name.
3687    ///
3688    /// Returns a string like "INTSXP", "REALSXP", etc.
3689    #[doc(alias = "type2char")]
3690    fn Rf_type2char(sexptype: SEXPTYPE) -> *const ::std::os::raw::c_char;
3691
3692    /// Print an R value to the console.
3693    ///
3694    /// Uses R's standard print method for the object.
3695    #[doc(alias = "PrintValue")]
3696    fn Rf_PrintValue(x: SEXP);
3697
3698    // Environment functions
3699
3700    /// Create a new environment.
3701    ///
3702    /// # Parameters
3703    ///
3704    /// - `enclos`: Enclosing environment
3705    /// - `hash`: Whether to use a hash table
3706    /// - `size`: Initial hash table size (if hash is TRUE)
3707    pub(crate) fn R_NewEnv(enclos: SEXP, hash: Rboolean, size: ::std::os::raw::c_int) -> SEXP;
3708
3709    /// Check if a variable exists in an environment frame.
3710    ///
3711    /// Does not search enclosing environments.
3712    fn R_existsVarInFrame(rho: SEXP, symbol: SEXP) -> Rboolean;
3713
3714    /// Remove a variable from an environment frame.
3715    ///
3716    /// # Returns
3717    ///
3718    /// The removed value, or R_NilValue if not found.
3719    fn R_removeVarFromFrame(symbol: SEXP, env: SEXP) -> SEXP;
3720
3721    /// Get the top-level environment.
3722    ///
3723    /// Walks up enclosing environments until reaching a top-level env
3724    /// (global, namespace, or base).
3725    #[doc(alias = "topenv")]
3726    fn Rf_topenv(target: SEXP, envir: SEXP) -> SEXP;
3727
3728    // Matching functions
3729
3730    /// Match elements of first vector in second vector.
3731    ///
3732    /// Like R's `match()` function.
3733    ///
3734    /// # Parameters
3735    ///
3736    /// - `x`: Vector of values to match
3737    /// - `table`: Vector to match against
3738    /// - `nomatch`: Value to return for non-matches
3739    ///
3740    /// # Returns
3741    ///
3742    /// Integer vector of match positions (1-indexed, nomatch for non-matches).
3743    #[doc(alias = "match")]
3744    fn Rf_match(x: SEXP, table: SEXP, nomatch: ::std::os::raw::c_int) -> SEXP;
3745
3746    // Duplication and copying
3747
3748    /// Copy most attributes from source to target.
3749    ///
3750    /// Copies all attributes except names, dim, and dimnames.
3751    #[doc(alias = "copyMostAttrib")]
3752    fn Rf_copyMostAttrib(source: SEXP, target: SEXP);
3753
3754    /// Find first duplicated element.
3755    ///
3756    /// # Parameters
3757    ///
3758    /// - `x`: Vector to search
3759    /// - `fromLast`: If TRUE, search from end
3760    ///
3761    /// # Returns
3762    ///
3763    /// 0 if no duplicates, otherwise 1-indexed position of first duplicate.
3764    #[doc(alias = "any_duplicated")]
3765    fn Rf_any_duplicated(x: SEXP, fromLast: Rboolean) -> R_xlen_t;
3766
3767    // S4 functions
3768
3769    /// Convert to an S4 object.
3770    ///
3771    /// # Parameters
3772    ///
3773    /// - `object`: Object to convert
3774    /// - `flag`: Conversion flag
3775    #[doc(alias = "asS4")]
3776    fn Rf_asS4(object: SEXP, flag: Rboolean, complete: ::std::os::raw::c_int) -> SEXP;
3777
3778    /// Get the S3 class of an S4 object.
3779    #[doc(alias = "S3Class")]
3780    fn Rf_S3Class(object: SEXP) -> SEXP;
3781
3782    // Option access
3783
3784    /// Get an R option value.
3785    ///
3786    /// Equivalent to `getOption("name")` in R.
3787    ///
3788    /// # Parameters
3789    ///
3790    /// - `tag`: Symbol for option name
3791    #[doc(alias = "GetOption1")]
3792    fn Rf_GetOption1(tag: SEXP) -> SEXP;
3793
3794    /// Get the `digits` option.
3795    ///
3796    /// Returns the value of `getOption("digits")`.
3797    #[doc(alias = "GetOptionDigits")]
3798    fn Rf_GetOptionDigits() -> ::std::os::raw::c_int;
3799
3800    /// Get the `width` option.
3801    ///
3802    /// Returns the value of `getOption("width")`.
3803    #[doc(alias = "GetOptionWidth")]
3804    fn Rf_GetOptionWidth() -> ::std::os::raw::c_int;
3805
3806    // Factor functions
3807
3808    /// Check if a factor is ordered.
3809    #[doc(alias = "isOrdered")]
3810    fn Rf_isOrdered(s: SEXP) -> Rboolean;
3811
3812    /// Check if a factor is unordered.
3813    #[doc(alias = "isUnordered")]
3814    fn Rf_isUnordered(s: SEXP) -> Rboolean;
3815
3816    /// Check if a vector is unsorted.
3817    ///
3818    /// # Parameters
3819    ///
3820    /// - `x`: Vector to check
3821    /// - `strictly`: If TRUE, check for strictly increasing
3822    #[doc(alias = "isUnsorted")]
3823    fn Rf_isUnsorted(x: SEXP, strictly: Rboolean) -> ::std::os::raw::c_int;
3824
3825    // Expression and evaluation
3826
3827    /// Substitute in an expression.
3828    ///
3829    /// Like R's `substitute()` function.
3830    #[doc(alias = "substitute")]
3831    fn Rf_substitute(lang: SEXP, rho: SEXP) -> SEXP;
3832
3833    /// Set vector length.
3834    ///
3835    /// For short vectors (length < 2^31).
3836    #[doc(alias = "lengthgets")]
3837    fn Rf_lengthgets(x: SEXP, newlen: R_xlen_t) -> SEXP;
3838
3839    /// Set vector length (long vector version).
3840    #[doc(alias = "xlengthgets")]
3841    fn Rf_xlengthgets(x: SEXP, newlen: R_xlen_t) -> SEXP;
3842
3843    // Protection (indexed — see cost table in the "GC protection" region above)
3844
3845    /// Protect a SEXP and record its stack index for later `R_Reprotect`.
3846    ///
3847    /// **Cost: O(1)** — same array write as `Rf_protect`, plus stores the index.
3848    /// No allocation. Use when you need to replace a protected value in-place
3849    /// (e.g., inside a loop that allocates) without unprotect/re-protect churn.
3850    #[doc(alias = "PROTECT_WITH_INDEX")]
3851    pub fn R_ProtectWithIndex(s: SEXP, index: *mut ::std::os::raw::c_int);
3852
3853    /// Replace the SEXP at a previously recorded protect stack index.
3854    ///
3855    /// **Cost: O(1)** — direct array write (`R_PPStack[index] = s`). No allocation.
3856    ///
3857    /// # Safety
3858    ///
3859    /// `index` must be from a previous `R_ProtectWithIndex` call and the
3860    /// stack must not have been unprotected past that index.
3861    #[doc(alias = "REPROTECT")]
3862    pub fn R_Reprotect(s: SEXP, index: ::std::os::raw::c_int);
3863
3864    // Weak references
3865
3866    /// Create a weak reference.
3867    ///
3868    /// # Parameters
3869    ///
3870    /// - `key`: The key object (weak reference target)
3871    /// - `val`: The value to associate
3872    /// - `fin`: Finalizer function (or R_NilValue)
3873    /// - `onexit`: Whether to run finalizer on R exit
3874    fn R_MakeWeakRef(key: SEXP, val: SEXP, fin: SEXP, onexit: Rboolean) -> SEXP;
3875
3876    /// Create a weak reference with C finalizer.
3877    fn R_MakeWeakRefC(key: SEXP, val: SEXP, fin: R_CFinalizer_t, onexit: Rboolean) -> SEXP;
3878
3879    /// Get the key from a weak reference.
3880    fn R_WeakRefKey(w: SEXP) -> SEXP;
3881
3882    /// Get the value from a weak reference.
3883    fn R_WeakRefValue(w: SEXP) -> SEXP;
3884
3885    /// Run pending finalizers.
3886    fn R_RunPendingFinalizers();
3887
3888    // Conversion list/vector
3889
3890    /// Convert a pairlist to a generic vector (list).
3891    #[doc(alias = "PairToVectorList")]
3892    fn Rf_PairToVectorList(x: SEXP) -> SEXP;
3893
3894    /// Convert a generic vector (list) to a pairlist.
3895    #[doc(alias = "VectorToPairList")]
3896    fn Rf_VectorToPairList(x: SEXP) -> SEXP;
3897
3898    // Install with CHARSXP
3899
3900    /// Install a symbol from a CHARSXP.
3901    ///
3902    /// Like `Rf_install()` but takes a CHARSXP instead of C string.
3903    #[doc(alias = "installChar")]
3904    fn Rf_installChar(x: SEXP) -> SEXP;
3905}
3906
3907// endregion