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