Skip to main content

miniextendr_api/
sys.rs

1//! Raw FFI bindings to R headers.
2//!
3//! This module mirrors R's C API closely and is intentionally thin. **You
4//! almost never call these directly from user code** — prefer the
5//! higher-level wrappers: `SEXP` (in [`crate::sexp`]), [`SexpExt`] (in
6//! [`crate::sexp_ext`]), the type vocabulary in [`crate::sexp_types`], plus
7//! [`IntoR`], [`TryFromSexp`], [`with_r_thread`], [`with_r_unwind_protect`],
8//! and the `#[miniextendr]` proc-macro. The items here exist so those
9//! wrappers can be written; treat them as the framework's escape hatch.
10//!
11//! [`SexpExt`]: crate::sexp_ext::SexpExt
12//! [`IntoR`]: crate::IntoR
13//! [`TryFromSexp`]: crate::TryFromSexp
14//! [`with_r_thread`]: crate::worker::with_r_thread
15//! [`with_r_unwind_protect`]: crate::unwind_protect::with_r_unwind_protect
16//!
17//! # Checked vs `*_unchecked` variants
18//!
19//! Most non-variadic R API entry points come in two forms thanks to the
20//! [`#[r_ffi_checked]`](miniextendr_macros::r_ffi_checked) proc-macro applied
21//! to the `unsafe extern "C-unwind"` blocks below:
22//!
23//! - **Checked** (default — e.g. `Rf_allocVector`, `Rf_protect`, `INTEGER`):
24//!   debug-asserts you're on R's main thread, routing through
25//!   [`crate::worker::with_r_thread`] when called from a worker thread. **Use
26//!   these by default.**
27//! - **`*_unchecked`** (e.g. `Rf_allocVector_unchecked`): bypass the assertion
28//!   and the worker round-trip. Calling one off the R main thread is
29//!   undefined behaviour. They exist for three known-safe contexts:
30//!     1. Inside ALTREP callbacks — R is already calling us on the main thread.
31//!     2. Inside a [`crate::unwind_protect::with_r_unwind_protect`] body —
32//!        the guard has already established main-thread context.
33//!     3. Inside a [`crate::worker::with_r_thread`] body — the check would be
34//!        redundant.
35//!
36//! The build-time lint **MXL301** enforces this: any `*_unchecked` call
37//! outside those contexts is a compile-time error.
38//!
39//! # Don't raise R errors directly
40//!
41//! `Rf_error`, `Rf_errorcall`, and their `_unchecked` siblings longjmp,
42//! which **skips Rust destructors** and leaks resources. The lint **MXL300**
43//! forbids them in user code. Use `panic!()` instead; the framework converts
44//! the panic into a structured R condition with `rust_*` class layering via
45//! the tagged-SEXP transport (see [`crate::error_value`]).
46//!
47//! # Cross references
48//!
49//! - [`crate::ffi_guard`] — guard taxonomy and worker-thread invariants.
50//! - [`crate::thread`] / [`crate::worker`] — worker / main-thread split.
51//! - [`crate::altrep_traits`] / [`crate::altrep_bridge`] — guard modes
52//!   inside ALTREP callbacks.
53//! - [`crate::error_value`] / [`mod@crate::condition`] — panic → R condition
54//!   transport.
55
56/// Raw ALTREP C API method type aliases.
57pub mod altrep;
58
59// `extern "C-unwind"` signatures below reference the type vocabulary by
60// bare name. Bring it into scope as a private `use` (NOT `pub use`) — the
61// vocab's canonical home is the crate root (`crate::SEXP`, etc.) and
62// `crate::sexp_types` for the niche helpers. Adding these to the public
63// API of `sys::` would re-create the bridge.
64use crate::sexp::SEXP;
65use crate::sexp_types::{R_CFinalizer_t, R_xlen_t, Rboolean, Rbyte, Rcomplex, SEXPTYPE, cetype_t};
66
67// region: Connections types (gated behind `connections` feature)
68// WARNING: R's connection API is explicitly marked as UNSTABLE.
69
70/// Opaque R connection implementation (from R_ext/Connections.h).
71///
72/// This is an opaque type representing R's internal connection structure.
73/// The actual structure is explicitly unstable and may change between R versions.
74#[cfg(feature = "connections")]
75#[repr(C)]
76#[allow(non_camel_case_types)]
77pub struct Rconnection_impl(::std::os::raw::c_void);
78
79/// Pointer to an R connection handle.
80///
81/// This is the typed equivalent of R's `Rconnection` type, which is a pointer
82/// to the opaque `Rconn` struct. Using this instead of `*mut c_void` provides
83/// type safety for connection APIs.
84#[cfg(feature = "connections")]
85#[allow(non_camel_case_types)]
86pub type Rconnection = *mut Rconnection_impl;
87
88/// R connections API version from R's `R_ext/Connections.h` at compile time.
89///
90/// This is a compile-time constant baked into the Rust FFI bindings when they
91/// were generated against a particular R version's headers. It does **not**
92/// dynamically probe the running R session.
93///
94/// From R_ext/Connections.h: "you *must* check the version and proceed only
95/// if it matches what you expect. We explicitly reserve the right to change
96/// the connection implementation without a compatibility layer."
97///
98/// Before using any connection APIs, check that this equals the expected version (1).
99#[cfg(feature = "connections")]
100#[allow(non_upper_case_globals)]
101pub const R_CONNECTIONS_VERSION: ::std::os::raw::c_int = 1;
102
103// endregion
104
105use miniextendr_macros::r_ffi_checked;
106
107// Unchecked variadic functions (internal use only, no thread check)
108#[allow(clashing_extern_declarations)]
109#[allow(varargs_without_pattern)]
110unsafe extern "C-unwind" {
111    /// Unchecked variadic `Rf_error`; call checked wrapper when possible.
112    #[link_name = "Rf_error"]
113    pub fn Rf_error_unchecked(arg1: *const ::std::os::raw::c_char, ...) -> !;
114    /// Unchecked variadic `Rf_errorcall`; call checked wrapper when possible.
115    #[link_name = "Rf_errorcall"]
116    pub fn Rf_errorcall_unchecked(arg1: SEXP, arg2: *const ::std::os::raw::c_char, ...) -> !;
117    /// Unchecked variadic `Rf_warning`; call checked wrapper when possible.
118    #[link_name = "Rf_warning"]
119    pub fn Rf_warning_unchecked(arg1: *const ::std::os::raw::c_char, ...);
120    /// Unchecked variadic `Rprintf`; call checked wrapper when possible.
121    #[link_name = "Rprintf"]
122    pub fn Rprintf_unchecked(arg1: *const ::std::os::raw::c_char, ...);
123    /// Unchecked variadic `REprintf`; call checked wrapper when possible.
124    #[link_name = "REprintf"]
125    pub fn REprintf_unchecked(arg1: *const ::std::os::raw::c_char, ...);
126}
127
128// Error message access (non-API, declared in Rinternals.h but flagged by R CMD check)
129#[cfg(feature = "nonapi")]
130unsafe extern "C-unwind" {
131    /// Get the current R error message buffer.
132    ///
133    /// Returns a pointer to R's internal error message buffer.
134    /// Used by Rserve and other embedding applications.
135    ///
136    /// # Safety
137    ///
138    /// - The returned pointer is only valid until the next R error
139    /// - Must not be modified
140    /// - Should be copied if needed beyond the immediate scope
141    ///
142    /// # Feature Gate
143    ///
144    /// This is a non-API function and requires the `nonapi` feature.
145    #[allow(non_snake_case, dead_code)] // used by worker.rs under worker-thread feature
146    pub(crate) fn R_curErrorBuf() -> *const ::std::os::raw::c_char;
147}
148
149// Console hooks (non-API; declared in Rinterface.h)
150#[cfg(feature = "nonapi")]
151unsafe extern "C-unwind" {
152    #[expect(dead_code, reason = "declared for future use")]
153    pub(crate) static ptr_R_WriteConsoleEx: Option<
154        unsafe extern "C-unwind" fn(
155            *const ::std::os::raw::c_char,
156            ::std::os::raw::c_int,
157            ::std::os::raw::c_int,
158        ),
159    >;
160}
161
162/// Checked wrapper for `Rf_error` - panics if called from non-main thread.
163/// Common usage: `Rf_error(c"%s".as_ptr(), message.as_ptr())`
164///
165/// # Safety
166///
167/// - Must be called from the R main thread
168/// - `fmt` and `arg1` must be valid null-terminated C strings
169#[inline(always)]
170#[allow(non_snake_case)]
171pub unsafe fn Rf_error(
172    fmt: *const ::std::os::raw::c_char,
173    arg1: *const ::std::os::raw::c_char,
174) -> ! {
175    if !crate::worker::is_r_main_thread() {
176        panic!("Rf_error called from non-main thread");
177    }
178    unsafe { Rf_error_unchecked(fmt, arg1) }
179}
180
181/// Checked wrapper for `Rf_errorcall` - panics if called from non-main thread.
182///
183/// # Safety
184///
185/// - Must be called from the R main thread
186/// - `call` must be a valid SEXP or R_NilValue
187/// - `fmt` and `arg1` must be valid null-terminated C strings
188#[inline(always)]
189#[allow(non_snake_case)]
190pub unsafe fn Rf_errorcall(
191    call: SEXP,
192    fmt: *const ::std::os::raw::c_char,
193    arg1: *const ::std::os::raw::c_char,
194) -> ! {
195    if !crate::worker::is_r_main_thread() {
196        panic!("Rf_errorcall called from non-main thread");
197    }
198    unsafe { Rf_errorcall_unchecked(call, fmt, arg1) }
199}
200
201/// Checked wrapper for `Rf_warning` - panics if called from non-main thread.
202///
203/// # Safety
204///
205/// - Must be called from the R main thread
206/// - `fmt` and `arg1` must be valid null-terminated C strings
207#[inline(always)]
208#[allow(non_snake_case)]
209pub unsafe fn Rf_warning(fmt: *const ::std::os::raw::c_char, arg1: *const ::std::os::raw::c_char) {
210    if !crate::worker::is_r_main_thread() {
211        panic!("Rf_warning called from non-main thread");
212    }
213    unsafe { Rf_warning_unchecked(fmt, arg1) }
214}
215
216/// Checked wrapper for `Rprintf` - panics if called from non-main thread.
217///
218/// # Safety
219///
220/// - Must be called from the R main thread
221/// - `fmt` and `arg1` must be valid null-terminated C strings
222#[inline(always)]
223#[allow(non_snake_case)]
224pub unsafe fn Rprintf(fmt: *const ::std::os::raw::c_char, arg1: *const ::std::os::raw::c_char) {
225    if !crate::worker::is_r_main_thread() {
226        panic!("Rprintf called from non-main thread");
227    }
228    unsafe { Rprintf_unchecked(fmt, arg1) }
229}
230
231/// Print to R's stderr (via R_ShowMessage or error console).
232///
233/// # Safety
234///
235/// - Must be called from the R main thread
236/// - `fmt` and `arg1` must be valid null-terminated C strings
237#[inline(always)]
238#[allow(non_snake_case)]
239pub unsafe fn REprintf(fmt: *const ::std::os::raw::c_char, arg1: *const ::std::os::raw::c_char) {
240    if !crate::worker::is_r_main_thread() {
241        panic!("REprintf called from non-main thread");
242    }
243    unsafe { REprintf_unchecked(fmt, arg1) }
244}
245
246// Imported R symbols and functions with runtime thread checks enabled.
247#[allow(missing_docs)]
248#[r_ffi_checked]
249#[allow(clashing_extern_declarations)]
250unsafe extern "C-unwind" {
251    /// The canonical R `NULL` value.
252    pub static R_NilValue: SEXP;
253
254    #[doc(alias = "NA_STRING")]
255    /// Missing string singleton — encapsulated by SEXP::na_string()
256    pub static R_NaString: SEXP;
257    /// Empty string CHARSXP — encapsulated by SEXP::blank_string()
258    pub static R_BlankString: SEXP;
259    /// Symbol for `names` attribute.
260    // Attribute symbols — encapsulated by SexpExt methods and SEXP::*_symbol()
261    pub static R_NamesSymbol: SEXP;
262    pub static R_DimSymbol: SEXP;
263    pub static R_DimNamesSymbol: SEXP;
264    pub static R_ClassSymbol: SEXP;
265    pub static R_RowNamesSymbol: SEXP;
266    pub static R_LevelsSymbol: SEXP;
267    pub static R_TspSymbol: SEXP;
268
269    /// Global environment (`.GlobalEnv`).
270    pub static R_GlobalEnv: SEXP;
271    /// Base package namespace environment.
272    pub static R_BaseEnv: SEXP;
273    /// Empty root environment.
274    pub static R_EmptyEnv: SEXP;
275    /// Base package namespace — encapsulated by SEXP::base_namespace()
276    pub static R_BaseNamespace: SEXP;
277
278    /// The "missing argument" sentinel value.
279    ///
280    /// When an R function is called without providing a value for a formal
281    /// argument, R passes `R_MissingArg` as a placeholder. This is different
282    /// from `R_NilValue` (NULL) - a missing argument means "not provided",
283    /// while NULL is an explicit value.
284    ///
285    /// In R: `f <- function(x) missing(x); f()` returns `TRUE`.
286    /// Encapsulated by SEXP::missing_arg()
287    pub static R_MissingArg: SEXP;
288
289    /// Sentinel returned by `Rf_findVarInFrame`/`Rf_findVarInFrame3` when the
290    /// symbol has no binding in the searched frame(s).
291    pub static R_UnboundValue: SEXP;
292
293    // Issue #112 cat. 10: kept pub(crate) — single-caller utilities; wrapping adds no value
294    // Rinterface.h
295    pub(crate) fn R_FlushConsole();
296
297    // Special logical values (from internal Defn.h, not public API)
298    // These are gated behind `nonapi` feature as they may change across R versions.
299    #[cfg(feature = "nonapi")]
300    /// Non-API TRUE singleton.
301    pub static R_TrueValue: SEXP;
302    #[cfg(feature = "nonapi")]
303    /// Non-API FALSE singleton.
304    pub static R_FalseValue: SEXP;
305    #[cfg(feature = "nonapi")]
306    /// Non-API NA logical singleton.
307    pub static R_LogicalNAValue: SEXP;
308
309    // Rinternals.h
310    #[doc(alias = "mkChar")]
311    pub fn Rf_mkChar(s: *const ::std::os::raw::c_char) -> SEXP;
312    #[doc(alias = "mkCharLen")]
313    pub fn Rf_mkCharLen(s: *const ::std::os::raw::c_char, len: i32) -> SEXP;
314    #[doc(alias = "mkCharLenCE")]
315    pub fn Rf_mkCharLenCE(
316        x: *const ::std::os::raw::c_char,
317        len: ::std::os::raw::c_int,
318        ce: cetype_t,
319    ) -> SEXP;
320    #[doc(alias = "xlength")]
321    #[doc(alias = "XLENGTH")]
322    pub fn Rf_xlength(x: SEXP) -> R_xlen_t;
323    #[doc(alias = "translateCharUTF8")]
324    pub fn Rf_translateCharUTF8(x: SEXP) -> *const ::std::os::raw::c_char;
325    #[doc(alias = "getCharCE")]
326    pub fn Rf_getCharCE(x: SEXP) -> cetype_t;
327    #[doc(alias = "charIsASCII")]
328    pub fn Rf_charIsASCII(x: SEXP) -> Rboolean;
329    #[doc(alias = "charIsUTF8")]
330    pub fn Rf_charIsUTF8(x: SEXP) -> Rboolean;
331    #[doc(alias = "charIsLatin1")]
332    pub fn Rf_charIsLatin1(x: SEXP) -> Rboolean;
333
334    // Issue #112 cat. 3: kept pub(crate) — only called from unwind_protect.rs; users go through with_r_unwind_protect
335    pub(crate) fn R_MakeUnwindCont() -> SEXP;
336    pub(crate) fn R_ContinueUnwind(cont: SEXP) -> !;
337    pub(crate) fn R_UnwindProtect(
338        fun: ::std::option::Option<
339            unsafe extern "C-unwind" fn(*mut ::std::os::raw::c_void) -> SEXP,
340        >,
341        fun_data: *mut ::std::os::raw::c_void,
342        cleanfun: ::std::option::Option<
343            unsafe extern "C-unwind" fn(*mut ::std::os::raw::c_void, Rboolean),
344        >,
345        cleanfun_data: *mut ::std::os::raw::c_void,
346        cont: SEXP,
347    ) -> SEXP;
348
349    /// Version of `R_UnwindProtect` that accepts `extern "C-unwind"` function pointers
350    #[link_name = "R_UnwindProtect"]
351    pub(crate) fn R_UnwindProtect_C_unwind(
352        fun: ::std::option::Option<
353            unsafe extern "C-unwind" fn(*mut ::std::os::raw::c_void) -> SEXP,
354        >,
355        fun_data: *mut ::std::os::raw::c_void,
356        cleanfun: ::std::option::Option<
357            unsafe extern "C-unwind" fn(*mut ::std::os::raw::c_void, Rboolean),
358        >,
359        cleanfun_data: *mut ::std::os::raw::c_void,
360        cont: SEXP,
361    ) -> SEXP;
362
363    // Rinternals.h
364    // Issue #112 cat. 2: kept pub(crate) — ExternalPtr<T> encapsulates these for users; raw access needed within externalptr.rs
365    #[doc = " External pointer interface"]
366    pub(crate) fn R_MakeExternalPtr(p: *mut ::std::os::raw::c_void, tag: SEXP, prot: SEXP) -> SEXP;
367    pub fn R_ExternalPtrAddr(s: SEXP) -> *mut ::std::os::raw::c_void;
368    pub(crate) fn R_ExternalPtrTag(s: SEXP) -> SEXP;
369    pub(crate) fn R_ExternalPtrProtected(s: SEXP) -> SEXP;
370    pub(crate) fn R_ClearExternalPtr(s: SEXP);
371    pub(crate) fn R_SetExternalPtrAddr(s: SEXP, p: *mut ::std::os::raw::c_void);
372    pub(crate) fn R_SetExternalPtrTag(s: SEXP, tag: SEXP);
373    pub(crate) fn R_SetExternalPtrProtected(s: SEXP, p: SEXP);
374    #[doc = " Added in R 3.4.0"]
375    pub fn R_MakeExternalPtrFn(p: DL_FUNC, tag: SEXP, prot: SEXP) -> SEXP;
376    pub fn R_ExternalPtrAddrFn(s: SEXP) -> DL_FUNC;
377    pub fn R_RegisterFinalizer(s: SEXP, fun: SEXP);
378    pub(crate) fn R_RegisterCFinalizer(s: SEXP, fun: R_CFinalizer_t);
379    pub fn R_RegisterFinalizerEx(s: SEXP, fun: SEXP, onexit: Rboolean);
380    pub(crate) fn R_RegisterCFinalizerEx(s: SEXP, fun: R_CFinalizer_t, onexit: Rboolean);
381
382    // R_ext/Rdynload.h - C-callable interface
383    // Issue #112 cat. 10: kept pub(crate) — cross-package ABI helpers used from mx_abi.rs; wrapping adds no value
384    /// Register a C-callable function for cross-package access.
385    pub(crate) fn R_RegisterCCallable(
386        package: *const ::std::os::raw::c_char,
387        name: *const ::std::os::raw::c_char,
388        fptr: DL_FUNC,
389    );
390    /// Get a C-callable function from another package.
391    pub(crate) fn R_GetCCallable(
392        package: *const ::std::os::raw::c_char,
393        name: *const ::std::os::raw::c_char,
394    ) -> DL_FUNC;
395
396    // region: GC protection
397    //
398    // R has two GC protection mechanisms with very different cost profiles:
399    //
400    // ## Protect stack (`Rf_protect` / `Rf_unprotect`)
401    //
402    // A pre-allocated array (`R_PPStack`) with an integer index (`R_PPStackTop`).
403    // Protect pushes: `R_PPStack[R_PPStackTop++] = s`.
404    // Unprotect pops: `R_PPStackTop -= n`.
405    // **No heap allocation. No GC pressure. Essentially a single memory write.**
406    // Use this for temporary protection within a function.
407    // Requires LIFO discipline — nested scopes are fine, interleaved are not.
408    //
409    // ## Precious list (`R_PreserveObject` / `R_ReleaseObject`)
410    //
411    // A global linked list of CONSXP cells (`R_PreciousList`).
412    // Preserve: `CONS(object, R_PreciousList)` — **allocates a cons cell every call**.
413    // Release: linear scan of the entire list to find and unlink the object — **O(n)**.
414    // (Optional `R_HASH_PRECIOUS` env var enables a 1069-bucket hash table, improving
415    // Release to O(bucket_size), but Preserve still allocates.)
416    // Use this only for long-lived objects that outlive any single protect scope.
417    //
418    // ## Cost summary
419    //
420    // | Operation            | Cost               | Allocates? |
421    // |----------------------|--------------------|------------|
422    // | `Rf_protect`         | array write        | no         |
423    // | `Rf_unprotect(n)`    | integer subtract   | no         |
424    // | `Rf_unprotect_ptr`   | scan + shift       | no         |
425    // | `R_PreserveObject`   | cons cell alloc    | **yes**    |
426    // | `R_ReleaseObject`    | linked list scan   | no (O(n))  |
427    // | `R_ProtectWithIndex` | array write + save | no         |
428    // | `R_Reprotect`        | array index write  | no         |
429
430    /// Add a SEXP to the protect stack, preventing GC collection.
431    ///
432    /// **Cost: O(1)** — single array write (`R_PPStack[top++] = s`). No allocation.
433    ///
434    /// Must be balanced by a corresponding `Rf_unprotect`. The protect stack is
435    /// LIFO — nested scopes are safe, but interleaved usage from different scopes
436    /// will cause incorrect unprotection.
437    #[doc(alias = "PROTECT")]
438    #[doc(alias = "protect")]
439    pub fn Rf_protect(s: SEXP) -> SEXP;
440
441    /// Pop the top `l` entries from the protect stack.
442    ///
443    /// **Cost: O(1)** — single integer subtract (`R_PPStackTop -= l`). No allocation.
444    ///
445    /// The popped SEXPs become eligible for GC. Must match the number of
446    /// `Rf_protect` calls in the current scope (LIFO order).
447    #[doc(alias = "UNPROTECT")]
448    #[doc(alias = "unprotect")]
449    pub fn Rf_unprotect(l: ::std::os::raw::c_int);
450
451    /// Remove a specific SEXP from anywhere in the protect stack.
452    ///
453    /// **Cost: O(k)** — scans backwards from top (k = distance from top), then
454    /// shifts remaining entries down. No allocation. R source comment:
455    /// *"should be among the top few items"*.
456    ///
457    /// Unlike `Rf_unprotect`, this is order-independent — it finds and removes
458    /// the specific pointer regardless of stack position. Useful when LIFO
459    /// discipline cannot be maintained, but more expensive than `Rf_unprotect`.
460    #[doc(alias = "UNPROTECT_PTR")]
461    pub fn Rf_unprotect_ptr(s: SEXP);
462
463    /// Add a SEXP to the global precious list, preventing GC indefinitely.
464    ///
465    /// **Cost: O(1) but allocates a CONSXP cell** — creates GC pressure on every
466    /// call. The precious list is a global linked list (`R_PreciousList`).
467    ///
468    /// Use only for long-lived objects (e.g., ExternalPtr stored across R calls).
469    /// For temporary protection within a function, prefer `Rf_protect`.
470    pub fn R_PreserveObject(object: SEXP);
471
472    /// Remove a SEXP from the global precious list, allowing GC.
473    ///
474    /// **Cost: O(n)** — linear scan of the entire precious list to find and unlink
475    /// the cons cell. With `R_HASH_PRECIOUS` env var, O(bucket_size) average
476    /// via a 1069-bucket hash table, but this is off by default.
477    pub fn R_ReleaseObject(object: SEXP);
478
479    // endregion
480    // Vector allocation functions
481    #[doc(alias = "allocVector")]
482    pub fn Rf_allocVector(sexptype: SEXPTYPE, length: R_xlen_t) -> SEXP;
483    #[doc(alias = "allocMatrix")]
484    pub fn Rf_allocMatrix(
485        sexptype: SEXPTYPE,
486        nrow: ::std::os::raw::c_int,
487        ncol: ::std::os::raw::c_int,
488    ) -> SEXP;
489    #[doc(alias = "allocArray")]
490    pub fn Rf_allocArray(sexptype: SEXPTYPE, dims: SEXP) -> SEXP;
491    #[doc(alias = "alloc3DArray")]
492    pub fn Rf_alloc3DArray(
493        sexptype: SEXPTYPE,
494        nrow: ::std::os::raw::c_int,
495        ncol: ::std::os::raw::c_int,
496        nface: ::std::os::raw::c_int,
497    ) -> SEXP;
498
499    // Pairlist allocation
500    // Issue #112 cat. 10: kept pub(crate) — 2 callers in expression.rs/dots.rs; wrapping adds no value
501    #[doc(alias = "allocList")]
502    pub(crate) fn Rf_allocList(n: ::std::os::raw::c_int) -> SEXP;
503    #[doc(alias = "allocLang")]
504    pub fn Rf_allocLang(n: ::std::os::raw::c_int) -> SEXP;
505    #[doc(alias = "allocS4Object")]
506    pub fn Rf_allocS4Object() -> SEXP;
507
508    // Pairlist construction — encapsulated by PairListExt trait
509    pub fn Rf_cons(car: SEXP, cdr: SEXP) -> SEXP;
510    pub fn Rf_lcons(car: SEXP, cdr: SEXP) -> SEXP;
511
512    // Attribute manipulation — encapsulated by SexpExt methods
513    #[doc(alias = "setAttrib")]
514    pub fn Rf_setAttrib(vec: SEXP, name: SEXP, val: SEXP) -> SEXP;
515
516    // Rinternals.h — scalar constructors; encapsulated by SEXP::scalar_*() / scalar_*_unchecked()
517    #[doc(alias = "ScalarComplex")]
518    pub fn Rf_ScalarComplex(x: Rcomplex) -> SEXP;
519    #[doc(alias = "ScalarInteger")]
520    pub fn Rf_ScalarInteger(x: ::std::os::raw::c_int) -> SEXP;
521    #[doc(alias = "ScalarLogical")]
522    pub fn Rf_ScalarLogical(x: ::std::os::raw::c_int) -> SEXP;
523    #[doc(alias = "ScalarRaw")]
524    pub fn Rf_ScalarRaw(x: Rbyte) -> SEXP;
525    #[doc(alias = "ScalarReal")]
526    pub fn Rf_ScalarReal(x: f64) -> SEXP;
527    #[doc(alias = "ScalarString")]
528    pub fn Rf_ScalarString(x: SEXP) -> SEXP;
529
530    // Rinternals.h
531    /// Non-API function - use DATAPTR_RO or DATAPTR_OR_NULL instead.
532    /// Only available with `nonapi` feature.
533    #[cfg(feature = "nonapi")]
534    pub(crate) fn DATAPTR(x: SEXP) -> *mut ::std::os::raw::c_void;
535    pub fn DATAPTR_RO(x: SEXP) -> *const ::std::os::raw::c_void;
536    pub fn DATAPTR_OR_NULL(x: SEXP) -> *const ::std::os::raw::c_void;
537
538    // region: Cons Cell (Pairlist) Accessors
539    //
540    // R's pairlists (LISTSXP) are cons cells like in Lisp/Scheme. Each node has:
541    // - CAR: The value/head element
542    // - CDR: The rest/tail of the list (another pairlist or R_NilValue)
543    // - TAG: An optional name (symbol) for named lists/arguments
544    //
545    // Example R pairlist: list(a = 1, b = 2, 3)
546    // - First node:  CAR=1,    TAG="a",  CDR=<next node>
547    // - Second node: CAR=2,    TAG="b",  CDR=<next node>
548    // - Third node:  CAR=3,    TAG=NULL, CDR=R_NilValue
549    //
550    // Pairlists are used for:
551    // - Function arguments (formal parameters and actual arguments)
552    // - Language objects (calls)
553    // - Dotted pairs in old-style lists
554    //
555    // The names CAR/CDR come from Lisp:
556    // - CAR = "Contents of Address part of Register"
557    // - CDR = "Contents of Decrement part of Register" (pronounced "could-er")
558    //
559    // Modern R mostly uses generic vectors (VECSXP) instead of pairlists,
560    // but pairlists are still used internally for function calls.
561
562    // Pairlist accessors — basic ops encapsulated by PairListExt trait,
563    // compound accessors (CAAR, CADR, etc.) module-private since no callers exist.
564    pub fn CAR(e: SEXP) -> SEXP;
565    pub fn CDR(e: SEXP) -> SEXP;
566    pub fn CAAR(e: SEXP) -> SEXP;
567    pub fn CDAR(e: SEXP) -> SEXP;
568    pub fn CADR(e: SEXP) -> SEXP;
569    pub fn CDDR(e: SEXP) -> SEXP;
570    pub fn CADDR(e: SEXP) -> SEXP;
571    pub fn CADDDR(e: SEXP) -> SEXP;
572    pub fn CAD4R(e: SEXP) -> SEXP;
573    pub fn TAG(e: SEXP) -> SEXP;
574    pub fn SET_TAG(x: SEXP, y: SEXP);
575    pub fn SETCAR(x: SEXP, y: SEXP) -> SEXP;
576    pub fn SETCDR(x: SEXP, y: SEXP) -> SEXP;
577    pub fn SETCADR(x: SEXP, y: SEXP) -> SEXP;
578    pub fn SETCADDR(x: SEXP, y: SEXP) -> SEXP;
579    pub fn SETCADDDR(x: SEXP, y: SEXP) -> SEXP;
580    pub fn SETCAD4R(e: SEXP, y: SEXP) -> SEXP;
581    pub fn LOGICAL_OR_NULL(x: SEXP) -> *const ::std::os::raw::c_int;
582    pub fn INTEGER_OR_NULL(x: SEXP) -> *const ::std::os::raw::c_int;
583    pub fn REAL_OR_NULL(x: SEXP) -> *const f64;
584    pub fn COMPLEX_OR_NULL(x: SEXP) -> *const Rcomplex;
585    pub fn RAW_OR_NULL(x: SEXP) -> *const Rbyte;
586
587    // Element-wise accessors (ALTREP-aware) — encapsulated by SexpExt methods
588    pub fn INTEGER_ELT(x: SEXP, i: R_xlen_t) -> ::std::os::raw::c_int;
589    pub fn REAL_ELT(x: SEXP, i: R_xlen_t) -> f64;
590    pub fn LOGICAL_ELT(x: SEXP, i: R_xlen_t) -> ::std::os::raw::c_int;
591    pub fn COMPLEX_ELT(x: SEXP, i: R_xlen_t) -> Rcomplex;
592    pub fn RAW_ELT(x: SEXP, i: R_xlen_t) -> Rbyte;
593    pub fn VECTOR_ELT(x: SEXP, i: R_xlen_t) -> SEXP;
594    pub fn STRING_ELT(x: SEXP, i: R_xlen_t) -> SEXP;
595    pub fn SET_STRING_ELT(x: SEXP, i: R_xlen_t, v: SEXP);
596    pub fn SET_LOGICAL_ELT(x: SEXP, i: R_xlen_t, v: ::std::os::raw::c_int);
597    pub fn SET_INTEGER_ELT(x: SEXP, i: R_xlen_t, v: ::std::os::raw::c_int);
598    pub fn SET_REAL_ELT(x: SEXP, i: R_xlen_t, v: f64);
599    pub fn SET_COMPLEX_ELT(x: SEXP, i: R_xlen_t, v: Rcomplex);
600    pub fn SET_RAW_ELT(x: SEXP, i: R_xlen_t, v: Rbyte);
601    pub fn SET_VECTOR_ELT(x: SEXP, i: R_xlen_t, v: SEXP) -> SEXP;
602
603    // endregion
604
605    // region: SEXP metadata accessors
606
607    /// Get the length of a SEXP as `int` (for short vectors < 2^31).
608    ///
609    /// For long vectors, use `Rf_xlength()` instead.
610    /// Returns 0 for R_NilValue.
611    pub fn LENGTH(x: SEXP) -> ::std::os::raw::c_int;
612
613    /// Get the length of a SEXP as `R_xlen_t` (supports long vectors).
614    ///
615    /// ALTREP-aware: will call ALTREP Length method if needed.
616    pub fn XLENGTH(x: SEXP) -> R_xlen_t;
617
618    /// Get the true length (allocated capacity) of a vector.
619    ///
620    /// May be larger than LENGTH for vectors with reserved space.
621    /// ALTREP-aware.
622    pub fn TRUELENGTH(x: SEXP) -> R_xlen_t;
623
624    /// Get the attributes pairlist of a SEXP.
625    ///
626    /// Returns R_NilValue if no attributes.
627    pub fn ATTRIB(x: SEXP) -> SEXP;
628
629    /// Set the attributes pairlist of a SEXP.
630    ///
631    /// # Safety
632    ///
633    /// `v` must be a pairlist or R_NilValue
634    pub fn SET_ATTRIB(x: SEXP, v: SEXP);
635
636    /// Check if SEXP has the "object" bit set (has a class).
637    ///
638    /// Returns non-zero if object has a class attribute.
639    pub fn OBJECT(x: SEXP) -> ::std::os::raw::c_int;
640
641    /// Set the "object" bit.
642    pub fn SET_OBJECT(x: SEXP, v: ::std::os::raw::c_int);
643
644    /// Get the LEVELS field (for factors).
645    pub fn LEVELS(x: SEXP) -> ::std::os::raw::c_int;
646
647    /// Set the LEVELS field (for factors).
648    ///
649    /// Returns the value that was set.
650    pub fn SETLEVELS(x: SEXP, v: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
651
652    // endregion
653
654    // region: ALTREP support — data2 encapsulated by AltrepSexpExt; data1 via standalone helpers
655
656    // Issue #112 cat. 6: pub(crate) — no AltrepSexpExt method yet; available for future callers
657    pub(crate) fn ALTREP_CLASS(x: SEXP) -> SEXP;
658    pub fn R_altrep_data1(x: SEXP) -> SEXP;
659    pub fn R_altrep_data2(x: SEXP) -> SEXP;
660    pub fn R_set_altrep_data1(x: SEXP, v: SEXP);
661    pub fn R_set_altrep_data2(x: SEXP, v: SEXP);
662
663    /// Check if a SEXP is an ALTREP object (returns non-zero if true).
664    ///
665    /// Use `SexpExt::is_altrep()` instead of calling this directly.
666    pub fn ALTREP(x: SEXP) -> ::std::os::raw::c_int;
667
668    // endregion
669
670    // region: Vector data accessors (mutable pointers)
671    // Issue #112 cat. 5: kept pub(crate) — raw pointer access needed in RNativeType impls and scattered callers;
672    //   partial migration to SexpExt::as_mut_slice() tracked in follow-up issue
673
674    /// Get mutable pointer to logical vector data.
675    ///
676    /// For ALTREP vectors, this may force materialization.
677    /// Get mutable pointer to logical vector data.
678    ///
679    /// For ALTREP vectors, this may force materialization.
680    /// Prefer `SexpExt::set_logical_elt()` / `SexpExt::logical_elt()`.
681    pub(crate) fn LOGICAL(x: SEXP) -> *mut ::std::os::raw::c_int;
682
683    /// Get mutable pointer to integer vector data.
684    ///
685    /// For ALTREP vectors, this may force materialization.
686    /// Prefer `SexpExt::set_integer_elt()` / `SexpExt::integer_elt()`.
687    pub(crate) fn INTEGER(x: SEXP) -> *mut ::std::os::raw::c_int;
688
689    /// Get mutable pointer to real vector data.
690    ///
691    /// For ALTREP vectors, this may force materialization.
692    /// Prefer `SexpExt::set_real_elt()` / `SexpExt::real_elt()`.
693    pub(crate) fn REAL(x: SEXP) -> *mut f64;
694
695    /// Get mutable pointer to complex vector data.
696    ///
697    /// For ALTREP vectors, this may force materialization.
698    /// Prefer `SexpExt::set_complex_elt()` / `SexpExt::complex_elt()`.
699    pub(crate) fn COMPLEX(x: SEXP) -> *mut Rcomplex;
700
701    /// Get mutable pointer to raw vector data.
702    ///
703    /// For ALTREP vectors, this may force materialization.
704    /// Prefer `SexpExt::set_raw_elt()` / `SexpExt::raw_elt()`.
705    pub(crate) fn RAW(x: SEXP) -> *mut Rbyte;
706
707    // endregion
708
709    // region: User interrupt and utilities
710
711    // utils.h
712    pub fn R_CheckUserInterrupt();
713
714    // endregion
715
716    // region: Type checking — encapsulated by SexpExt::type_of()
717
718    pub fn TYPEOF(x: SEXP) -> SEXPTYPE;
719
720    // endregion
721
722    // Symbol creation and access
723    #[doc(alias = "install")]
724    pub fn Rf_install(name: *const ::std::os::raw::c_char) -> SEXP;
725    /// Get the print name (CHARSXP) of a symbol (SYMSXP)
726    pub fn PRINTNAME(x: SEXP) -> SEXP;
727    /// Get the C string pointer from a CHARSXP — encapsulated by SexpExt::r_char()
728    #[doc(alias = "CHAR")]
729    pub fn R_CHAR(x: SEXP) -> *const ::std::os::raw::c_char;
730
731    // Attribute access
732    // Attribute accessors — encapsulated by SexpExt methods
733    /// Read an attribute from an object by symbol (e.g. `R_NamesSymbol`).
734    ///
735    /// Returns `R_NilValue` if the attribute is not set.
736    #[doc(alias = "getAttrib")]
737    pub fn Rf_getAttrib(vec: SEXP, name: SEXP) -> SEXP;
738    /// Set the `names` attribute; returns the updated object.
739    #[doc(alias = "namesgets")]
740    pub fn Rf_namesgets(vec: SEXP, val: SEXP) -> SEXP;
741    /// Set the `dim` attribute; returns the updated object.
742    #[doc(alias = "dimgets")]
743    pub fn Rf_dimgets(vec: SEXP, val: SEXP) -> SEXP;
744
745    // Duplication
746    #[doc(alias = "duplicate")]
747    pub fn Rf_duplicate(s: SEXP) -> SEXP;
748    #[doc(alias = "shallow_duplicate")]
749    pub fn Rf_shallow_duplicate(s: SEXP) -> SEXP;
750
751    // Object comparison
752    /// Check if two R objects are identical (deep semantic equality).
753    ///
754    /// This is the C implementation of R's `identical()` function.
755    ///
756    /// # Flags
757    ///
758    /// Use the `IDENT_*` constants below. Flags are inverted: set bit = disable that check.
759    ///
760    /// **Default from R**: `IDENT_USE_CLOENV` (16) - ignore closure environments
761    ///
762    /// # Returns
763    ///
764    /// `TRUE` if identical, `FALSE` otherwise.
765    ///
766    /// # Performance
767    ///
768    /// Fast-path: Returns `TRUE` immediately if pointers are equal.
769    pub fn R_compute_identical(x: SEXP, y: SEXP, flags: ::std::os::raw::c_int) -> Rboolean;
770}
771
772/// Flags for `R_compute_identical` (bitmask, inverted logic: set bit = disable check).
773pub const IDENT_NUM_AS_BITS: ::std::os::raw::c_int = 1;
774/// Treat all NAs as identical (ignore NA payload differences).
775pub const IDENT_NA_AS_BITS: ::std::os::raw::c_int = 2;
776/// Compare attributes in order (not as a set).
777pub const IDENT_ATTR_BY_ORDER: ::std::os::raw::c_int = 4;
778/// Include bytecode in comparison.
779pub const IDENT_USE_BYTECODE: ::std::os::raw::c_int = 8;
780/// Include closure environments in comparison.
781pub const IDENT_USE_CLOENV: ::std::os::raw::c_int = 16;
782/// Include source references in comparison.
783pub const IDENT_USE_SRCREF: ::std::os::raw::c_int = 32;
784/// Compare external pointers as references (not by address).
785pub const IDENT_EXTPTR_AS_REF: ::std::os::raw::c_int = 64;
786
787// Additional checked R API declarations used by conversion and reflection code.
788#[allow(missing_docs)]
789#[r_ffi_checked]
790unsafe extern "C-unwind" {
791    // Type coercion — encapsulated by SexpExt methods
792    #[doc(alias = "asLogical")]
793    pub fn Rf_asLogical(x: SEXP) -> ::std::os::raw::c_int;
794    #[doc(alias = "asInteger")]
795    pub fn Rf_asInteger(x: SEXP) -> ::std::os::raw::c_int;
796    #[doc(alias = "asReal")]
797    pub fn Rf_asReal(x: SEXP) -> f64;
798    #[doc(alias = "asChar")]
799    pub fn Rf_asChar(x: SEXP) -> SEXP;
800    #[doc(alias = "coerceVector")]
801    pub fn Rf_coerceVector(v: SEXP, sexptype: SEXPTYPE) -> SEXP;
802
803    // Matrix utilities — no callers outside ffi.rs
804    #[doc(alias = "nrows")]
805    pub fn Rf_nrows(x: SEXP) -> ::std::os::raw::c_int;
806    #[doc(alias = "ncols")]
807    pub fn Rf_ncols(x: SEXP) -> ::std::os::raw::c_int;
808
809    // Inheritance checking — encapsulated by SexpExt::inherits_class()
810    #[doc(alias = "inherits")]
811    pub fn Rf_inherits(x: SEXP, klass: *const ::std::os::raw::c_char) -> Rboolean;
812
813    // Type checking predicates — encapsulated by SexpExt type-check methods
814    #[doc(alias = "isNull")]
815    pub fn Rf_isNull(s: SEXP) -> Rboolean;
816    #[doc(alias = "isSymbol")]
817    pub fn Rf_isSymbol(s: SEXP) -> Rboolean;
818    #[doc(alias = "isLogical")]
819    pub fn Rf_isLogical(s: SEXP) -> Rboolean;
820    #[doc(alias = "isReal")]
821    pub fn Rf_isReal(s: SEXP) -> Rboolean;
822    #[doc(alias = "isComplex")]
823    pub fn Rf_isComplex(s: SEXP) -> Rboolean;
824    #[doc(alias = "isExpression")]
825    pub fn Rf_isExpression(s: SEXP) -> Rboolean;
826    #[doc(alias = "isEnvironment")]
827    pub fn Rf_isEnvironment(s: SEXP) -> Rboolean;
828    #[doc(alias = "isString")]
829    pub fn Rf_isString(s: SEXP) -> Rboolean;
830
831    // Composite type checking (from inline functions)
832    #[doc(alias = "isArray")]
833    pub fn Rf_isArray(s: SEXP) -> Rboolean;
834    #[doc(alias = "isMatrix")]
835    pub fn Rf_isMatrix(s: SEXP) -> Rboolean;
836    #[doc(alias = "isList")]
837    pub fn Rf_isList(s: SEXP) -> Rboolean;
838    #[doc(alias = "isNewList")]
839    pub fn Rf_isNewList(s: SEXP) -> Rboolean;
840    #[doc(alias = "isPairList")]
841    pub fn Rf_isPairList(s: SEXP) -> Rboolean;
842    #[doc(alias = "isFunction")]
843    pub fn Rf_isFunction(s: SEXP) -> Rboolean;
844    #[doc(alias = "isPrimitive")]
845    pub fn Rf_isPrimitive(s: SEXP) -> Rboolean;
846    #[doc(alias = "isLanguage")]
847    pub fn Rf_isLanguage(s: SEXP) -> Rboolean;
848    #[doc(alias = "isDataFrame")]
849    pub fn Rf_isDataFrame(s: SEXP) -> Rboolean;
850    #[doc(alias = "isFactor")]
851    pub fn Rf_isFactor(s: SEXP) -> Rboolean;
852    #[doc(alias = "isInteger")]
853    pub fn Rf_isInteger(s: SEXP) -> Rboolean;
854    #[doc(alias = "isObject")]
855    pub fn Rf_isObject(s: SEXP) -> Rboolean;
856
857    // Pairlist utilities
858    #[doc(alias = "elt")]
859    pub fn Rf_elt(list: SEXP, i: ::std::os::raw::c_int) -> SEXP;
860    #[doc(alias = "lastElt")]
861    pub fn Rf_lastElt(list: SEXP) -> SEXP;
862    #[doc(alias = "nthcdr")]
863    pub fn Rf_nthcdr(list: SEXP, n: ::std::os::raw::c_int) -> SEXP;
864    #[doc(alias = "listAppend")]
865    pub fn Rf_listAppend(s: SEXP, t: SEXP) -> SEXP;
866
867    // More attribute setters (using R's "gets" suffix convention)
868    //
869    // See "Attribute access" section above for explanation of the "gets" suffix.
870    // These are setter functions equivalent to R's `attr(x) <- value` syntax.
871
872    /// Set the class attribute of a vector.
873    ///
874    /// Equivalent to R's `class(vec) <- klass` syntax.
875    /// The "gets" suffix indicates this is a setter function.
876    ///
877    /// # Returns
878    ///
879    /// Returns the modified vector (like all "*gets" functions).
880    #[doc(alias = "classgets")]
881    pub fn Rf_classgets(vec: SEXP, klass: SEXP) -> SEXP;
882
883    /// Set the dimnames attribute of an array/matrix.
884    ///
885    /// Equivalent to R's `dimnames(vec) <- val` syntax.
886    /// The "gets" suffix indicates this is a setter function.
887    ///
888    /// # Returns
889    ///
890    /// Returns the modified vector.
891    #[doc(alias = "dimnamesgets")]
892    pub fn Rf_dimnamesgets(vec: SEXP, val: SEXP) -> SEXP;
893    // Issue #112 cat. 10: kept pub(crate) — 2 callers each in factor.rs/matrix helpers; wrapping adds no value
894    #[doc(alias = "GetRowNames")]
895    pub(crate) fn Rf_GetRowNames(dimnames: SEXP) -> SEXP;
896    #[doc(alias = "GetColNames")]
897    pub(crate) fn Rf_GetColNames(dimnames: SEXP) -> SEXP;
898
899    // Environment operations
900    #[doc(alias = "findVar")]
901    pub fn Rf_findVar(symbol: SEXP, rho: SEXP) -> SEXP;
902    #[doc(alias = "findVarInFrame")]
903    pub fn Rf_findVarInFrame(rho: SEXP, symbol: SEXP) -> SEXP;
904    #[doc(alias = "findVarInFrame3")]
905    pub fn Rf_findVarInFrame3(rho: SEXP, symbol: SEXP, doget: Rboolean) -> SEXP;
906    #[doc(alias = "defineVar")]
907    pub fn Rf_defineVar(symbol: SEXP, value: SEXP, rho: SEXP);
908    #[doc(alias = "setVar")]
909    pub fn Rf_setVar(symbol: SEXP, value: SEXP, rho: SEXP);
910    #[doc(alias = "findFun")]
911    pub fn Rf_findFun(symbol: SEXP, rho: SEXP) -> SEXP;
912
913    /// Find a registered namespace by name. **Longjmps on error** — prefer
914    /// `REnv::package_namespace()` which wraps this safely.
915    #[doc(alias = "FindNamespace")]
916    pub fn R_FindNamespace(info: SEXP) -> SEXP;
917
918    // Issue #112 cat. 9: kept pub(crate) — R_GetCurrentEnv used from s4_helpers.rs; R_tryEvalSilent from expression.rs
919    /// Return the current execution environment (innermost closure on call
920    /// stack, or `R_GlobalEnv` if none).
921    #[doc(alias = "GetCurrentEnv")]
922    pub(crate) fn R_GetCurrentEnv() -> SEXP;
923
924    // Evaluation
925    #[doc(alias = "eval")]
926    pub fn Rf_eval(expr: SEXP, rho: SEXP) -> SEXP;
927    #[doc(alias = "applyClosure")]
928    pub fn Rf_applyClosure(
929        call: SEXP,
930        op: SEXP,
931        args: SEXP,
932        rho: SEXP,
933        suppliedvars: SEXP,
934        check: Rboolean,
935    ) -> SEXP;
936    pub fn R_tryEval(expr: SEXP, env: SEXP, error_occurred: *mut ::std::os::raw::c_int) -> SEXP;
937    pub(crate) fn R_tryEvalSilent(
938        expr: SEXP,
939        env: SEXP,
940        error_occurred: *mut ::std::os::raw::c_int,
941    ) -> SEXP;
942    pub fn R_forceAndCall(e: SEXP, n: ::std::os::raw::c_int, rho: SEXP) -> SEXP;
943
944    /// Parse R source text into an EXPRSXP (a list of parsed expressions).
945    ///
946    /// `text` is a STRSXP holding the source, `n` is the number of expressions
947    /// to parse (`-1` for all), `status` receives the [`ParseStatus`] outcome,
948    /// and `srcfile` is a srcref/`R_NilValue`. Allocates; protect the result.
949    ///
950    /// Prefer the safe [`crate::expression::r_eval_str`] wrapper, which does the
951    /// STRSXP construction, status check, and protection bookkeeping for you.
952    #[doc(alias = "ParseVector")]
953    pub fn R_ParseVector(
954        text: SEXP,
955        n: ::std::os::raw::c_int,
956        status: *mut ParseStatus,
957        srcfile: SEXP,
958    ) -> SEXP;
959}
960
961/// Outcome of [`R_ParseVector`] (from `R_ext/Parse.h`).
962///
963/// `PARSE_NULL` is never returned by `R_ParseVector`; the meaningful success
964/// value is [`ParseStatus::PARSE_OK`]. The remaining variants indicate parse
965/// failures (`PARSE_ERROR`), incomplete input (`PARSE_INCOMPLETE`), or
966/// end-of-input (`PARSE_EOF`).
967#[allow(non_camel_case_types)]
968#[repr(i32)]
969#[derive(Debug, Clone, Copy, PartialEq, Eq)]
970pub enum ParseStatus {
971    /// Never returned by `R_ParseVector`; the default-initialized sentinel.
972    PARSE_NULL,
973    /// Parse succeeded.
974    PARSE_OK,
975    /// Input ended mid-expression (e.g. an unbalanced delimiter).
976    PARSE_INCOMPLETE,
977    /// A syntax error was encountered.
978    PARSE_ERROR,
979    /// End of input reached with no further expressions.
980    PARSE_EOF,
981}
982
983// region: Connections API (R_ext/Connections.h)
984//
985// Gated behind `connections` feature because R's connection API is explicitly UNSTABLE.
986// From R_ext/Connections.h:
987//   "IMPORTANT: we do not expect future connection APIs to be
988//    backward-compatible so if you use this, you *must* check the
989//    version and proceeds only if it matches what you expect.
990//
991//    We explicitly reserve the right to change the connection
992//    implementation without a compatibility layer."
993//
994// Use with caution and always check R_CONNECTIONS_VERSION.
995// Issue #112 cat. 8: kept pub(crate) — feature-gated behind `connections`; behind Connection type for users
996#[r_ffi_checked]
997#[cfg(feature = "connections")]
998unsafe extern "C-unwind" {
999    /// Create a new custom connection.
1000    ///
1001    /// # WARNING
1002    ///
1003    /// This API is UNSTABLE. Check `R_CONNECTIONS_VERSION` before use.
1004    /// The connection implementation may change without notice.
1005    ///
1006    /// # Safety
1007    ///
1008    /// - `description`, `mode`, and `class_name` must be valid C strings
1009    /// - `ptr` must be a valid pointer to store the connection handle
1010    pub(crate) fn R_new_custom_connection(
1011        description: *const ::std::os::raw::c_char,
1012        mode: *const ::std::os::raw::c_char,
1013        class_name: *const ::std::os::raw::c_char,
1014        ptr: *mut Rconnection,
1015    ) -> SEXP;
1016
1017    /// Read from a connection.
1018    ///
1019    /// # WARNING
1020    ///
1021    /// This API is UNSTABLE and may change.
1022    ///
1023    /// # Safety
1024    ///
1025    /// - `con` must be a valid Rconnection handle
1026    /// - `buf` must be a valid buffer with at least `n` bytes
1027    pub(crate) fn R_ReadConnection(
1028        con: Rconnection,
1029        buf: *mut ::std::os::raw::c_void,
1030        n: usize,
1031    ) -> usize;
1032
1033    /// Write to a connection.
1034    ///
1035    /// # WARNING
1036    ///
1037    /// This API is UNSTABLE and may change.
1038    ///
1039    /// # Safety
1040    ///
1041    /// - `con` must be a valid Rconnection handle
1042    /// - `buf` must contain at least `n` valid bytes
1043    pub(crate) fn R_WriteConnection(
1044        con: Rconnection,
1045        buf: *const ::std::os::raw::c_void,
1046        n: usize,
1047    ) -> usize;
1048
1049    /// Get a connection from a SEXP.
1050    ///
1051    /// # WARNING
1052    ///
1053    /// This API is UNSTABLE and may change.
1054    /// Added in R 3.3.0.
1055    ///
1056    /// # Safety
1057    ///
1058    /// - `sConn` must be a valid connection SEXP
1059    pub(crate) fn R_GetConnection(sConn: SEXP) -> Rconnection;
1060}
1061// endregion: Connections API
1062
1063/// Check if a SEXP is an S4 object.
1064///
1065/// # Safety
1066///
1067/// - `arg1` must be a valid SEXP
1068#[allow(non_snake_case)]
1069pub unsafe fn Rf_isS4(arg1: SEXP) -> Rboolean {
1070    unsafe extern "C-unwind" {
1071        #[link_name = "Rf_isS4"]
1072        pub fn Rf_isS4_original(arg1: SEXP) -> u32;
1073    }
1074
1075    unsafe {
1076        if Rf_isS4_original(arg1) == 0 {
1077            Rboolean::FALSE
1078        } else {
1079            Rboolean::TRUE
1080        }
1081    }
1082}
1083
1084// region: registration!
1085
1086#[repr(C)]
1087#[derive(Debug)]
1088/// Opaque dynamic library descriptor from R.
1089pub struct DllInfo(::std::os::raw::c_void);
1090
1091/// Generic dynamic library function pointer.
1092///
1093/// R defines this as `void *(*)(void)` - a function taking no arguments and
1094/// returning `void*`. This is used for method registration and external pointer
1095/// functions. The actual function signatures vary; callers cast to the appropriate
1096/// concrete function type before calling.
1097///
1098/// We use `fn() -> *mut c_void` to match R's signature. The function pointer is
1099/// stored generically and cast to the appropriate type when called by R.
1100#[allow(non_camel_case_types)]
1101pub type DL_FUNC =
1102    ::std::option::Option<unsafe extern "C-unwind" fn() -> *mut ::std::os::raw::c_void>;
1103
1104/// Type descriptor for native primitive arguments in .C/.Fortran calls.
1105///
1106/// This is used in `R_CMethodDef` and `R_FortranMethodDef` to specify
1107/// argument types for type checking.
1108#[allow(non_camel_case_types)]
1109pub type R_NativePrimitiveArgType = ::std::os::raw::c_uint;
1110
1111/// Method definition for .C interface routines.
1112///
1113/// Used to register C functions callable via `.C()` from R.
1114#[repr(C)]
1115#[derive(Debug, Copy, Clone)]
1116#[allow(non_camel_case_types)]
1117#[allow(non_snake_case)]
1118pub struct R_CMethodDef {
1119    /// Exported symbol name.
1120    pub name: *const ::std::os::raw::c_char,
1121    /// Function pointer implementing the routine.
1122    pub fun: DL_FUNC,
1123    /// Declared arity.
1124    pub numArgs: ::std::os::raw::c_int,
1125    /// Optional array of argument types for type checking. May be null.
1126    pub types: *const R_NativePrimitiveArgType,
1127}
1128
1129/// Method definition for .Fortran interface routines.
1130///
1131/// Structurally identical to `R_CMethodDef`.
1132#[allow(non_camel_case_types)]
1133pub type R_FortranMethodDef = R_CMethodDef;
1134
1135/// Method definition for .Call interface routines.
1136///
1137/// Used to register C functions callable via `.Call()` from R.
1138/// Unlike `.C()` routines, `.Call()` functions receive and return SEXP values directly.
1139#[repr(C)]
1140#[derive(Debug, Copy, Clone)]
1141#[allow(non_camel_case_types)]
1142#[allow(non_snake_case)]
1143pub struct R_CallMethodDef {
1144    /// Exported symbol name.
1145    pub name: *const ::std::os::raw::c_char,
1146    /// Function pointer implementing the routine.
1147    pub fun: DL_FUNC,
1148    /// Declared arity.
1149    pub numArgs: ::std::os::raw::c_int,
1150}
1151
1152// SAFETY: `name` points to a static CStr literal, `fun` is a function pointer.
1153// Both are valid for program lifetime and safe to read from any thread.
1154unsafe impl Sync for R_CallMethodDef {}
1155unsafe impl Send for R_CallMethodDef {}
1156
1157/// Method definition for .External interface routines.
1158///
1159/// Structurally identical to `R_CallMethodDef`.
1160#[allow(non_camel_case_types)]
1161pub type R_ExternalMethodDef = R_CallMethodDef;
1162
1163// Checked routine registration API declarations.
1164// Issue #112 cat. 7: kept pub(crate) — only called from init.rs during package init; not worth a wrapper type
1165#[allow(missing_docs)]
1166#[r_ffi_checked]
1167#[allow(clashing_extern_declarations)]
1168unsafe extern "C-unwind" {
1169    pub(crate) fn R_registerRoutines(
1170        info: *mut DllInfo,
1171        croutines: *const R_CMethodDef,
1172        callRoutines: *const R_CallMethodDef,
1173        fortranRoutines: *const R_FortranMethodDef,
1174        externalRoutines: *const R_ExternalMethodDef,
1175    ) -> ::std::os::raw::c_int;
1176
1177    pub(crate) fn R_useDynamicSymbols(info: *mut DllInfo, value: Rboolean) -> Rboolean;
1178    pub(crate) fn R_forceSymbols(info: *mut DllInfo, value: Rboolean) -> Rboolean;
1179}
1180
1181// endregion
1182
1183// region: Non-API encoding/locale state (Defn.h)
1184
1185/// Non-API encoding / locale helpers from R's `Defn.h`.
1186///
1187/// These are not part of the stable R API and may break across R versions.
1188///
1189/// Only symbols R's shared library actually **exports** may be declared here.
1190/// `Defn.h` marks most locale globals `extern0` (= `attribute_hidden`) —
1191/// referencing one of those (e.g. `known_to_be_utf8`, `latin1locale`,
1192/// `R_nativeEncoding`) compiles fine but aborts `dyn.load` of any binary that
1193/// carries the reference: data relocations resolve eagerly at load, whether or
1194/// not the code path ever runs. That made every `nonapi` build un-loadable
1195/// (caught by the feature-legs CI, audit A5). `utf8locale`, `mbcslocale`, and
1196/// `known_to_be_latin1` are plain `extern` and exported (verified against
1197/// R 4.6's libR).
1198#[cfg(feature = "nonapi")]
1199pub mod nonapi_encoding {
1200    use super::r_ffi_checked;
1201
1202    // Issue #112 cat. 10: kept pub(crate) — nonapi encoding helpers; single-caller utilities in encoding.rs
1203    #[r_ffi_checked]
1204    #[allow(clashing_extern_declarations)]
1205    unsafe extern "C-unwind" {
1206        // Locale flags (exported, non-hidden)
1207        pub(crate) static utf8locale: super::Rboolean;
1208        pub(crate) static mbcslocale: super::Rboolean;
1209        pub(crate) static known_to_be_latin1: super::Rboolean;
1210    }
1211}
1212
1213// endregion
1214
1215// region: Non-API stack checking variables (Rinterface.h)
1216
1217/// Non-API stack checking variables from `Rinterface.h`.
1218///
1219/// R uses these to detect stack overflow. When calling R from a thread other
1220/// than the main R thread, stack checking will fail because these values are
1221/// set for the main thread's stack.
1222///
1223/// # Usage
1224///
1225/// To safely call R from a worker thread, disable stack checking:
1226/// ```ignore
1227/// #[cfg(feature = "nonapi")]
1228/// unsafe {
1229///     use miniextendr_api::sys::nonapi_stack::*;
1230///     let saved = get_r_cstack_limit();
1231///     set_r_cstack_limit(usize::MAX); // disable checking
1232///     // ... call R APIs ...
1233///     set_r_cstack_limit(saved); // restore
1234/// }
1235/// ```
1236///
1237/// Or use the higher-level [`StackCheckGuard`](crate::thread::StackCheckGuard) which handles this automatically.
1238///
1239/// Setting `R_CStackLimit` to `usize::MAX` (i.e., `-1` as `uintptr_t`) disables
1240/// stack checking entirely.
1241#[cfg(feature = "nonapi")]
1242pub mod nonapi_stack {
1243    unsafe extern "C" {
1244        /// Top of the stack (set during `Rf_initialize_R` for main thread).
1245        ///
1246        /// On Unix, determined via `__libc_stack_end`, `KERN_USRSTACK`, or
1247        /// `thr_stksegment`. On Windows, via `VirtualQuery`.
1248        #[allow(non_upper_case_globals)]
1249        pub(crate) static R_CStackStart: usize;
1250
1251        /// Stack size limit. Set to `usize::MAX` to disable stack checking.
1252        ///
1253        /// From R source: `if(R_CStackStart == -1) R_CStackLimit = -1; /* never set */`
1254        #[allow(non_upper_case_globals)]
1255        pub static R_CStackLimit: usize;
1256
1257        /// Stack growth direction: 1 = grows up, -1 = grows down.
1258        ///
1259        /// Most systems (x86, ARM) grow down (-1).
1260        #[allow(non_upper_case_globals)]
1261        pub(crate) static R_CStackDir: ::std::os::raw::c_int;
1262    }
1263
1264    /// Write to `R_CStackLimit`.
1265    ///
1266    /// # Safety
1267    /// Must be called from R's main thread.
1268    #[inline]
1269    pub unsafe fn set_r_cstack_limit(value: usize) {
1270        unsafe {
1271            let ptr = (&raw const R_CStackLimit).cast_mut();
1272            ptr.write(value);
1273        }
1274    }
1275
1276    // Issue #112 cat. 10: kept pub(crate) — nonapi stack helpers; used from thread.rs; wrapping adds no value
1277    /// Read `R_CStackLimit`.
1278    #[inline]
1279    pub(crate) fn get_r_cstack_limit() -> usize {
1280        unsafe { R_CStackLimit }
1281    }
1282
1283    /// Read `R_CStackStart`.
1284    #[inline]
1285    pub(crate) fn get_r_cstack_start() -> usize {
1286        unsafe { R_CStackStart }
1287    }
1288
1289    /// Read `R_CStackDir`.
1290    #[inline]
1291    pub(crate) fn get_r_cstack_dir() -> ::std::os::raw::c_int {
1292        unsafe { R_CStackDir }
1293    }
1294}
1295
1296// endregion
1297
1298// region: Inline Helper Functions (Rust implementations of R's inline functions)
1299
1300/// Create a length-1 string vector from a C string.
1301///
1302/// Rust equivalent of R's inline `Rf_mkString(s)`, which is
1303/// shorthand for `ScalarString(mkChar(s))`.
1304///
1305/// # Safety
1306///
1307/// - `s` must be a valid null-terminated C string
1308/// - Must be called from R's main thread
1309/// - Result must be protected from GC
1310#[doc(alias = "mkString")]
1311#[allow(non_snake_case)]
1312#[inline]
1313pub unsafe fn Rf_mkString(s: *const ::std::os::raw::c_char) -> SEXP {
1314    unsafe {
1315        let charsxp = Rf_mkChar(s);
1316        let protected = Rf_protect(charsxp);
1317        let result = Rf_ScalarString(protected);
1318        Rf_unprotect(1);
1319        result
1320    }
1321}
1322
1323/// Build a pairlist with 1 element.
1324///
1325/// Rust equivalent of R's inline `Rf_list1(s)`.
1326///
1327/// # Safety
1328///
1329/// - `s` must be a valid SEXP
1330/// - Must be called from R's main thread
1331/// - Result must be protected from GC
1332#[doc(alias = "list1")]
1333#[allow(non_snake_case)]
1334#[inline]
1335pub unsafe fn Rf_list1(s: SEXP) -> SEXP {
1336    unsafe { Rf_cons(s, R_NilValue) }
1337}
1338
1339/// Build a pairlist with 2 elements.
1340///
1341/// Rust equivalent of R's inline `Rf_list2(s, t)`.
1342///
1343/// # Safety
1344///
1345/// - Both SEXPs must be valid
1346/// - Must be called from R's main thread
1347/// - Result must be protected from GC
1348#[doc(alias = "list2")]
1349#[allow(non_snake_case)]
1350#[inline]
1351pub unsafe fn Rf_list2(s: SEXP, t: SEXP) -> SEXP {
1352    unsafe { Rf_cons(s, Rf_cons(t, R_NilValue)) }
1353}
1354
1355/// Build a pairlist with 3 elements.
1356///
1357/// Rust equivalent of R's inline `Rf_list3(s, t, u)`.
1358///
1359/// # Safety
1360///
1361/// - All SEXPs must be valid
1362/// - Must be called from R's main thread
1363/// - Result must be protected from GC
1364#[doc(alias = "list3")]
1365#[allow(non_snake_case)]
1366#[inline]
1367pub unsafe fn Rf_list3(s: SEXP, t: SEXP, u: SEXP) -> SEXP {
1368    unsafe { Rf_cons(s, Rf_cons(t, Rf_cons(u, R_NilValue))) }
1369}
1370
1371/// Build a pairlist with 4 elements.
1372///
1373/// Rust equivalent of R's inline `Rf_list4(s, t, u, v)`.
1374///
1375/// # Safety
1376///
1377/// - All SEXPs must be valid
1378/// - Must be called from R's main thread
1379/// - Result must be protected from GC
1380#[doc(alias = "list4")]
1381#[allow(non_snake_case)]
1382#[inline]
1383pub unsafe fn Rf_list4(s: SEXP, t: SEXP, u: SEXP, v: SEXP) -> SEXP {
1384    unsafe { Rf_cons(s, Rf_cons(t, Rf_cons(u, Rf_cons(v, R_NilValue)))) }
1385}
1386
1387/// Build a language object (call) with 1 element (the function).
1388///
1389/// Rust equivalent of R's inline `Rf_lang1(s)`.
1390/// Creates a call like `f()` where `s` is the function.
1391///
1392/// # Safety
1393///
1394/// - `s` must be a valid SEXP (typically a symbol or closure)
1395/// - Must be called from R's main thread
1396/// - Result must be protected from GC
1397#[doc(alias = "lang1")]
1398#[allow(non_snake_case)]
1399#[inline]
1400pub unsafe fn Rf_lang1(s: SEXP) -> SEXP {
1401    unsafe { Rf_lcons(s, R_NilValue) }
1402}
1403
1404/// Build a language object (call) with function and 1 argument.
1405///
1406/// Rust equivalent of R's inline `Rf_lang2(s, t)`.
1407/// Creates a call like `f(arg)` where `s` is the function and `t` is the argument.
1408///
1409/// # Safety
1410///
1411/// - Both SEXPs must be valid
1412/// - Must be called from R's main thread
1413/// - Result must be protected from GC
1414#[doc(alias = "lang2")]
1415#[allow(non_snake_case)]
1416#[inline]
1417pub unsafe fn Rf_lang2(s: SEXP, t: SEXP) -> SEXP {
1418    unsafe { Rf_lcons(s, Rf_list1(t)) }
1419}
1420
1421/// Build a language object (call) with function and 2 arguments.
1422///
1423/// Rust equivalent of R's inline `Rf_lang3(s, t, u)`.
1424/// Creates a call like `f(arg1, arg2)`.
1425///
1426/// # Safety
1427///
1428/// - All SEXPs must be valid
1429/// - Must be called from R's main thread
1430/// - Result must be protected from GC
1431#[doc(alias = "lang3")]
1432#[allow(non_snake_case)]
1433#[inline]
1434pub unsafe fn Rf_lang3(s: SEXP, t: SEXP, u: SEXP) -> SEXP {
1435    unsafe { Rf_lcons(s, Rf_list2(t, u)) }
1436}
1437
1438/// Build a language object (call) with function and 3 arguments.
1439///
1440/// Rust equivalent of R's inline `Rf_lang4(s, t, u, v)`.
1441/// Creates a call like `f(arg1, arg2, arg3)`.
1442///
1443/// # Safety
1444///
1445/// - All SEXPs must be valid
1446/// - Must be called from R's main thread
1447/// - Result must be protected from GC
1448#[doc(alias = "lang4")]
1449#[allow(non_snake_case)]
1450#[inline]
1451pub unsafe fn Rf_lang4(s: SEXP, t: SEXP, u: SEXP, v: SEXP) -> SEXP {
1452    unsafe { Rf_lcons(s, Rf_list3(t, u, v)) }
1453}
1454
1455/// Build a language object (call) with function and 4 arguments.
1456///
1457/// Rust equivalent of R's inline `Rf_lang5(s, t, u, v, w)`.
1458/// Creates a call like `f(arg1, arg2, arg3, arg4)`.
1459///
1460/// # Safety
1461///
1462/// - All SEXPs must be valid
1463/// - Must be called from R's main thread
1464/// - Result must be protected from GC
1465#[doc(alias = "lang5")]
1466#[allow(non_snake_case)]
1467#[inline]
1468pub unsafe fn Rf_lang5(s: SEXP, t: SEXP, u: SEXP, v: SEXP, w: SEXP) -> SEXP {
1469    unsafe { Rf_lcons(s, Rf_list4(t, u, v, w)) }
1470}
1471
1472/// Build a language object (call) with function and 5 arguments.
1473///
1474/// Rust equivalent of R's inline `Rf_lang6(s, t, u, v, w, x)`.
1475/// Creates a call like `f(arg1, arg2, arg3, arg4, arg5)`.
1476///
1477/// # Safety
1478///
1479/// - All SEXPs must be valid
1480/// - Must be called from R's main thread
1481/// - Result must be protected from GC
1482#[doc(alias = "lang6")]
1483#[allow(non_snake_case)]
1484#[inline]
1485pub unsafe fn Rf_lang6(s: SEXP, t: SEXP, u: SEXP, v: SEXP, w: SEXP, x: SEXP) -> SEXP {
1486    unsafe {
1487        let protected = Rf_protect(s);
1488        let list = Rf_cons(t, Rf_list4(u, v, w, x));
1489        let result = Rf_lcons(protected, list);
1490        Rf_unprotect(1);
1491        result
1492    }
1493}
1494
1495// endregion
1496
1497// region: RNG functions (R_ext/Random.h)
1498
1499/// RNG type enum from R_ext/Random.h
1500#[repr(u32)]
1501#[non_exhaustive]
1502#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1503#[allow(non_camel_case_types)]
1504pub enum RNGtype {
1505    /// Wichmann-Hill generator.
1506    WICHMANN_HILL = 0,
1507    /// Marsaglia-Multicarry generator.
1508    MARSAGLIA_MULTICARRY = 1,
1509    /// Super-Duper generator.
1510    SUPER_DUPER = 2,
1511    /// Mersenne Twister generator.
1512    MERSENNE_TWISTER = 3,
1513    /// Knuth TAOCP generator.
1514    KNUTH_TAOCP = 4,
1515    /// User-supplied uniform generator.
1516    USER_UNIF = 5,
1517    /// Knuth TAOCP 2002 variant.
1518    KNUTH_TAOCP2 = 6,
1519    /// L'Ecuyer-CMRG generator.
1520    LECUYER_CMRG = 7,
1521}
1522
1523/// Normal distribution generator type enum from R_ext/Random.h
1524#[repr(u32)]
1525#[non_exhaustive]
1526#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1527#[allow(non_camel_case_types)]
1528pub enum N01type {
1529    /// Legacy buggy Kinderman-Ramage method.
1530    BUGGY_KINDERMAN_RAMAGE = 0,
1531    /// Ahrens-Dieter method.
1532    AHRENS_DIETER = 1,
1533    /// Box-Muller transform.
1534    BOX_MULLER = 2,
1535    /// User-supplied normal generator.
1536    USER_NORM = 3,
1537    /// Inversion method.
1538    INVERSION = 4,
1539    /// Fixed Kinderman-Ramage method.
1540    KINDERMAN_RAMAGE = 5,
1541}
1542
1543/// Discrete uniform sample method enum from R_ext/Random.h
1544#[repr(u32)]
1545#[non_exhaustive]
1546#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
1547#[allow(non_camel_case_types)]
1548pub enum Sampletype {
1549    /// Rounding method for integer sampling.
1550    ROUNDING = 0,
1551    /// Rejection sampling method.
1552    REJECTION = 1,
1553}
1554
1555#[r_ffi_checked]
1556unsafe extern "C-unwind" {
1557    /// Save the current RNG state from R's global state.
1558    ///
1559    /// Must be called before using `unif_rand()`, `norm_rand()`, etc.
1560    /// The state is restored with `PutRNGstate()`.
1561    ///
1562    /// # Example
1563    ///
1564    /// ```ignore
1565    /// unsafe {
1566    ///     GetRNGstate();
1567    ///     let x = unif_rand();
1568    ///     let y = norm_rand();
1569    ///     PutRNGstate();
1570    /// }
1571    /// ```
1572    pub fn GetRNGstate();
1573
1574    /// Restore the RNG state to R's global state.
1575    ///
1576    /// Must be called after using `unif_rand()`, `norm_rand()`, etc.
1577    /// to ensure R's `.Random.seed` is updated.
1578    pub fn PutRNGstate();
1579
1580    /// Generate a uniform random number in (0, 1).
1581    ///
1582    /// # Important
1583    ///
1584    /// Must call `GetRNGstate()` before and `PutRNGstate()` after.
1585    pub fn unif_rand() -> f64;
1586
1587    /// Generate a standard normal random number (mean 0, sd 1).
1588    ///
1589    /// # Important
1590    ///
1591    /// Must call `GetRNGstate()` before and `PutRNGstate()` after.
1592    pub fn norm_rand() -> f64;
1593
1594    /// Generate an exponential random number with rate 1.
1595    ///
1596    /// # Important
1597    ///
1598    /// Must call `GetRNGstate()` before and `PutRNGstate()` after.
1599    pub fn exp_rand() -> f64;
1600
1601    /// Generate a uniform random index in [0, dn).
1602    ///
1603    /// Used for sampling without bias for large n.
1604    ///
1605    /// # Important
1606    ///
1607    /// Must call `GetRNGstate()` before and `PutRNGstate()` after.
1608    pub fn R_unif_index(dn: f64) -> f64;
1609
1610    /// Get the current discrete uniform sample method.
1611    pub fn R_sample_kind() -> Sampletype;
1612}
1613
1614// endregion
1615
1616// region: Memory allocation (R_ext/Memory.h)
1617
1618#[r_ffi_checked]
1619unsafe extern "C-unwind" {
1620    /// Get the current R memory stack watermark.
1621    ///
1622    /// Use with `vmaxset()` to restore memory stack state.
1623    /// Memory allocated with `R_alloc()` between `vmaxget()` and `vmaxset()`
1624    /// will be freed when `vmaxset()` is called.
1625    ///
1626    /// # Example
1627    ///
1628    /// ```ignore
1629    /// unsafe {
1630    ///     let watermark = vmaxget();
1631    ///     let buf = R_alloc(100, 1);
1632    ///     // ... use buf ...
1633    ///     vmaxset(watermark); // frees buf
1634    /// }
1635    /// ```
1636    pub fn vmaxget() -> *mut ::std::os::raw::c_void;
1637
1638    /// Set the R memory stack watermark, freeing memory allocated since the mark.
1639    ///
1640    /// # Safety
1641    ///
1642    /// `ovmax` must be a value returned by `vmaxget()` called earlier in the
1643    /// same R evaluation context.
1644    pub fn vmaxset(ovmax: *const ::std::os::raw::c_void);
1645
1646    /// Run the R garbage collector.
1647    ///
1648    /// Forces a full garbage collection cycle.
1649    pub fn R_gc();
1650
1651    /// Check if the garbage collector is currently running.
1652    ///
1653    /// Returns non-zero if GC is in progress.
1654    pub fn R_gc_running() -> ::std::os::raw::c_int;
1655
1656    /// Allocate memory on R's memory stack.
1657    ///
1658    /// This memory is automatically freed when the calling R function returns,
1659    /// or can be freed earlier with `vmaxset()`.
1660    ///
1661    /// # Parameters
1662    ///
1663    /// - `nelem`: Number of elements to allocate
1664    /// - `eltsize`: Size of each element in bytes
1665    ///
1666    /// # Returns
1667    ///
1668    /// Pointer to allocated memory (as `char*` for compatibility with S).
1669    pub fn R_alloc(nelem: usize, eltsize: ::std::os::raw::c_int) -> *mut ::std::os::raw::c_char;
1670
1671    /// Allocate an array of long doubles on R's memory stack.
1672    ///
1673    /// # Parameters
1674    ///
1675    /// - `nelem`: Number of long double elements to allocate
1676    pub fn R_allocLD(nelem: usize) -> *mut f64; // Note: f64 is close enough for most uses
1677
1678    /// S compatibility: allocate zeroed memory on R's memory stack.
1679    ///
1680    /// # Parameters
1681    ///
1682    /// - `nelem`: Number of elements
1683    /// - `eltsize`: Size of each element
1684    pub fn S_alloc(
1685        nelem: ::std::os::raw::c_long,
1686        eltsize: ::std::os::raw::c_int,
1687    ) -> *mut ::std::os::raw::c_char;
1688
1689    /// S compatibility: reallocate memory on R's memory stack.
1690    ///
1691    /// # Safety
1692    ///
1693    /// `ptr` must have been allocated by `S_alloc`.
1694    pub fn S_realloc(
1695        ptr: *mut ::std::os::raw::c_char,
1696        newsize: ::std::os::raw::c_long,
1697        oldsize: ::std::os::raw::c_long,
1698        eltsize: ::std::os::raw::c_int,
1699    ) -> *mut ::std::os::raw::c_char;
1700
1701    /// GC-aware malloc.
1702    ///
1703    /// Triggers GC if allocation fails, then retries.
1704    /// Memory must be freed with `free()`.
1705    pub fn R_malloc_gc(size: usize) -> *mut ::std::os::raw::c_void;
1706
1707    /// GC-aware calloc.
1708    ///
1709    /// Triggers GC if allocation fails, then retries.
1710    /// Memory must be freed with `free()`.
1711    pub fn R_calloc_gc(nelem: usize, eltsize: usize) -> *mut ::std::os::raw::c_void;
1712
1713    /// GC-aware realloc.
1714    ///
1715    /// Triggers GC if allocation fails, then retries.
1716    /// Memory must be freed with `free()`.
1717    pub fn R_realloc_gc(
1718        ptr: *mut ::std::os::raw::c_void,
1719        size: usize,
1720    ) -> *mut ::std::os::raw::c_void;
1721}
1722
1723// endregion
1724
1725// region: Sorting and utility functions (R_ext/Utils.h)
1726
1727#[r_ffi_checked]
1728unsafe extern "C-unwind" {
1729    /// Sort an integer vector in place (ascending order).
1730    ///
1731    /// # Parameters
1732    ///
1733    /// - `x`: Pointer to integer array
1734    /// - `n`: Number of elements
1735    pub fn R_isort(x: *mut ::std::os::raw::c_int, n: ::std::os::raw::c_int);
1736
1737    /// Sort a double vector in place (ascending order).
1738    ///
1739    /// # Parameters
1740    ///
1741    /// - `x`: Pointer to double array
1742    /// - `n`: Number of elements
1743    pub fn R_rsort(x: *mut f64, n: ::std::os::raw::c_int);
1744
1745    /// Sort a complex vector in place.
1746    ///
1747    /// # Parameters
1748    ///
1749    /// - `x`: Pointer to Rcomplex array
1750    /// - `n`: Number of elements
1751    pub fn R_csort(x: *mut Rcomplex, n: ::std::os::raw::c_int);
1752
1753    /// Sort doubles in descending order, carrying along an index array.
1754    ///
1755    /// # Parameters
1756    ///
1757    /// - `a`: Pointer to double array (sorted in place, descending)
1758    /// - `ib`: Pointer to integer array (permuted alongside `a`)
1759    /// - `n`: Number of elements
1760    #[doc(alias = "Rf_revsort")]
1761    pub fn revsort(a: *mut f64, ib: *mut ::std::os::raw::c_int, n: ::std::os::raw::c_int);
1762
1763    /// Sort doubles with index array.
1764    ///
1765    /// # Parameters
1766    ///
1767    /// - `x`: Pointer to double array (sorted in place)
1768    /// - `indx`: Pointer to integer array (permuted alongside `x`)
1769    /// - `n`: Number of elements
1770    pub fn rsort_with_index(
1771        x: *mut f64,
1772        indx: *mut ::std::os::raw::c_int,
1773        n: ::std::os::raw::c_int,
1774    );
1775
1776    /// Partial sort integers (moves k-th smallest to position k).
1777    ///
1778    /// # Parameters
1779    ///
1780    /// - `x`: Pointer to integer array
1781    /// - `n`: Number of elements
1782    /// - `k`: Target position (0-indexed)
1783    #[doc(alias = "Rf_iPsort")]
1784    pub fn iPsort(
1785        x: *mut ::std::os::raw::c_int,
1786        n: ::std::os::raw::c_int,
1787        k: ::std::os::raw::c_int,
1788    );
1789
1790    /// Partial sort doubles (moves k-th smallest to position k).
1791    ///
1792    /// # Parameters
1793    ///
1794    /// - `x`: Pointer to double array
1795    /// - `n`: Number of elements
1796    /// - `k`: Target position (0-indexed)
1797    #[doc(alias = "Rf_rPsort")]
1798    pub fn rPsort(x: *mut f64, n: ::std::os::raw::c_int, k: ::std::os::raw::c_int);
1799
1800    /// Partial sort complex numbers.
1801    ///
1802    /// # Parameters
1803    ///
1804    /// - `x`: Pointer to Rcomplex array
1805    /// - `n`: Number of elements
1806    /// - `k`: Target position (0-indexed)
1807    #[doc(alias = "Rf_cPsort")]
1808    pub fn cPsort(x: *mut Rcomplex, n: ::std::os::raw::c_int, k: ::std::os::raw::c_int);
1809
1810    /// Quicksort doubles in place.
1811    ///
1812    /// # Parameters
1813    ///
1814    /// - `v`: Pointer to double array
1815    /// - `i`: Start index (1-indexed for R compatibility)
1816    /// - `j`: End index (1-indexed)
1817    pub fn R_qsort(v: *mut f64, i: usize, j: usize);
1818
1819    /// Quicksort doubles with index array.
1820    ///
1821    /// # Parameters
1822    ///
1823    /// - `v`: Pointer to double array
1824    /// - `indx`: Pointer to index array (permuted alongside v)
1825    /// - `i`: Start index (1-indexed)
1826    /// - `j`: End index (1-indexed)
1827    pub fn R_qsort_I(
1828        v: *mut f64,
1829        indx: *mut ::std::os::raw::c_int,
1830        i: ::std::os::raw::c_int,
1831        j: ::std::os::raw::c_int,
1832    );
1833
1834    /// Quicksort integers in place.
1835    ///
1836    /// # Parameters
1837    ///
1838    /// - `iv`: Pointer to integer array
1839    /// - `i`: Start index (1-indexed)
1840    /// - `j`: End index (1-indexed)
1841    pub fn R_qsort_int(iv: *mut ::std::os::raw::c_int, i: usize, j: usize);
1842
1843    /// Quicksort integers with index array.
1844    ///
1845    /// # Parameters
1846    ///
1847    /// - `iv`: Pointer to integer array
1848    /// - `indx`: Pointer to index array
1849    /// - `i`: Start index (1-indexed)
1850    /// - `j`: End index (1-indexed)
1851    pub fn R_qsort_int_I(
1852        iv: *mut ::std::os::raw::c_int,
1853        indx: *mut ::std::os::raw::c_int,
1854        i: ::std::os::raw::c_int,
1855        j: ::std::os::raw::c_int,
1856    );
1857
1858    /// Expand a filename, resolving `~` and environment variables.
1859    ///
1860    /// # Returns
1861    ///
1862    /// Pointer to expanded path (in R's internal buffer, do not free).
1863    pub fn R_ExpandFileName(s: *const ::std::os::raw::c_char) -> *const ::std::os::raw::c_char;
1864
1865    /// Convert string to double, always using '.' as decimal point.
1866    ///
1867    /// Also accepts "NA" as input, returning NA_REAL.
1868    pub fn R_atof(str: *const ::std::os::raw::c_char) -> f64;
1869
1870    /// Convert string to double with end pointer, using '.' as decimal point.
1871    ///
1872    /// Like `strtod()` but locale-independent.
1873    pub fn R_strtod(c: *const ::std::os::raw::c_char, end: *mut *mut ::std::os::raw::c_char)
1874    -> f64;
1875
1876    /// Generate a temporary filename.
1877    ///
1878    /// # Parameters
1879    ///
1880    /// - `prefix`: Filename prefix
1881    /// - `tempdir`: Directory for temp file
1882    ///
1883    /// # Returns
1884    ///
1885    /// Newly allocated string (must be freed with `R_free_tmpnam`).
1886    pub fn R_tmpnam(
1887        prefix: *const ::std::os::raw::c_char,
1888        tempdir: *const ::std::os::raw::c_char,
1889    ) -> *mut ::std::os::raw::c_char;
1890
1891    /// Generate a temporary filename with extension.
1892    ///
1893    /// # Parameters
1894    ///
1895    /// - `prefix`: Filename prefix
1896    /// - `tempdir`: Directory for temp file
1897    /// - `fileext`: File extension (e.g., ".txt")
1898    ///
1899    /// # Returns
1900    ///
1901    /// Newly allocated string (must be freed with `R_free_tmpnam`).
1902    pub fn R_tmpnam2(
1903        prefix: *const ::std::os::raw::c_char,
1904        tempdir: *const ::std::os::raw::c_char,
1905        fileext: *const ::std::os::raw::c_char,
1906    ) -> *mut ::std::os::raw::c_char;
1907
1908    /// Free a temporary filename allocated by `R_tmpnam` or `R_tmpnam2`.
1909    pub fn R_free_tmpnam(name: *mut ::std::os::raw::c_char);
1910
1911    /// Check for R stack overflow.
1912    ///
1913    /// Throws an R error if stack is nearly exhausted.
1914    pub fn R_CheckStack();
1915
1916    /// Check for R stack overflow with extra space requirement.
1917    ///
1918    /// # Parameters
1919    ///
1920    /// - `extra`: Additional bytes needed
1921    pub fn R_CheckStack2(extra: usize);
1922
1923    /// Find the interval containing a value (binary search).
1924    ///
1925    /// Used for interpolation and binning.
1926    ///
1927    /// # Parameters
1928    ///
1929    /// - `xt`: Sorted breakpoints array
1930    /// - `n`: Number of breakpoints
1931    /// - `x`: Value to find
1932    /// - `rightmost_closed`: If TRUE, rightmost interval is closed
1933    /// - `all_inside`: If TRUE, out-of-bounds values map to endpoints
1934    /// - `ilo`: Initial guess for interval (1-indexed)
1935    /// - `mflag`: Output flag (see R documentation)
1936    ///
1937    /// # Returns
1938    ///
1939    /// Interval index (1-indexed).
1940    pub fn findInterval(
1941        xt: *const f64,
1942        n: ::std::os::raw::c_int,
1943        x: f64,
1944        rightmost_closed: Rboolean,
1945        all_inside: Rboolean,
1946        ilo: ::std::os::raw::c_int,
1947        mflag: *mut ::std::os::raw::c_int,
1948    ) -> ::std::os::raw::c_int;
1949
1950    /// Extended interval finding with left-open option.
1951    #[allow(clippy::too_many_arguments)]
1952    pub fn findInterval2(
1953        xt: *const f64,
1954        n: ::std::os::raw::c_int,
1955        x: f64,
1956        rightmost_closed: Rboolean,
1957        all_inside: Rboolean,
1958        left_open: Rboolean,
1959        ilo: ::std::os::raw::c_int,
1960        mflag: *mut ::std::os::raw::c_int,
1961    ) -> ::std::os::raw::c_int;
1962
1963    /// Find column maxima in a matrix.
1964    ///
1965    /// # Parameters
1966    ///
1967    /// - `matrix`: Column-major matrix data
1968    /// - `nr`: Number of rows
1969    /// - `nc`: Number of columns
1970    /// - `maxes`: Output array for column maxima indices (1-indexed)
1971    /// - `ties_meth`: How to handle ties (1=first, 2=random, 3=last)
1972    pub fn R_max_col(
1973        matrix: *const f64,
1974        nr: *const ::std::os::raw::c_int,
1975        nc: *const ::std::os::raw::c_int,
1976        maxes: *mut ::std::os::raw::c_int,
1977        ties_meth: *const ::std::os::raw::c_int,
1978    );
1979
1980    /// Check if a string represents FALSE in R.
1981    ///
1982    /// Recognizes "FALSE", "false", "False", "F", "f", etc.
1983    #[doc(alias = "Rf_StringFalse")]
1984    pub fn StringFalse(s: *const ::std::os::raw::c_char) -> Rboolean;
1985
1986    /// Check if a string represents TRUE in R.
1987    ///
1988    /// Recognizes "TRUE", "true", "True", "T", "t", etc.
1989    #[doc(alias = "Rf_StringTrue")]
1990    pub fn StringTrue(s: *const ::std::os::raw::c_char) -> Rboolean;
1991
1992    /// Check if a string is blank (empty or only whitespace).
1993    #[doc(alias = "Rf_isBlankString")]
1994    pub fn isBlankString(s: *const ::std::os::raw::c_char) -> Rboolean;
1995}
1996
1997// endregion
1998
1999// region: Additional Rinternals.h functions
2000
2001#[r_ffi_checked]
2002unsafe extern "C-unwind" {
2003    // String/character functions
2004
2005    /// Create a CHARSXP with specified encoding.
2006    ///
2007    /// # Parameters
2008    ///
2009    /// - `s`: C string
2010    /// - `encoding`: Character encoding (CE_UTF8, CE_LATIN1, etc.)
2011    // Issue #112 cat. 10: kept pub(crate) — 2 callers in encoding.rs; wrapping adds no value
2012    #[doc(alias = "mkCharCE")]
2013    pub(crate) fn Rf_mkCharCE(s: *const ::std::os::raw::c_char, encoding: cetype_t) -> SEXP;
2014
2015    /// Get the number of characters in a string/character.
2016    ///
2017    /// # Parameters
2018    ///
2019    /// - `x`: A string SEXP
2020    /// - `ntype`: Type of count (0=bytes, 1=chars, 2=width)
2021    /// - `allowNA`: Whether to allow NA values
2022    /// - `keepNA`: Whether to keep NA in result
2023    /// - `msg_name`: Name for error messages
2024    ///
2025    /// # Returns
2026    ///
2027    /// Character count or -1 on error.
2028    pub fn R_nchar(
2029        x: SEXP,
2030        ntype: ::std::os::raw::c_int,
2031        allowNA: Rboolean,
2032        keepNA: Rboolean,
2033        msg_name: *const ::std::os::raw::c_char,
2034    ) -> ::std::os::raw::c_int;
2035
2036    /// Convert SEXPTYPE to C string name.
2037    ///
2038    /// Returns a string like "INTSXP", "REALSXP", etc.
2039    #[doc(alias = "type2char")]
2040    pub fn Rf_type2char(sexptype: SEXPTYPE) -> *const ::std::os::raw::c_char;
2041
2042    /// Print an R value to the console.
2043    ///
2044    /// Uses R's standard print method for the object.
2045    #[doc(alias = "PrintValue")]
2046    pub fn Rf_PrintValue(x: SEXP);
2047
2048    // Environment functions
2049
2050    /// Create a new environment.
2051    ///
2052    /// # Parameters
2053    ///
2054    /// - `enclos`: Enclosing environment
2055    /// - `hash`: Whether to use a hash table
2056    /// - `size`: Initial hash table size (if hash is TRUE)
2057    // Issue #112 cat. 10: kept pub(crate) — 2 callers in environment.rs; wrapping adds no value
2058    pub(crate) fn R_NewEnv(enclos: SEXP, hash: Rboolean, size: ::std::os::raw::c_int) -> SEXP;
2059
2060    /// Check if a variable exists in an environment frame.
2061    ///
2062    /// Does not search enclosing environments.
2063    pub fn R_existsVarInFrame(rho: SEXP, symbol: SEXP) -> Rboolean;
2064
2065    /// Remove a variable from an environment frame.
2066    ///
2067    /// # Returns
2068    ///
2069    /// The removed value, or R_NilValue if not found.
2070    pub fn R_removeVarFromFrame(symbol: SEXP, env: SEXP) -> SEXP;
2071
2072    /// Get the top-level environment.
2073    ///
2074    /// Walks up enclosing environments until reaching a top-level env
2075    /// (global, namespace, or base).
2076    #[doc(alias = "topenv")]
2077    pub fn Rf_topenv(target: SEXP, envir: SEXP) -> SEXP;
2078
2079    // Matching functions
2080
2081    /// Match elements of first vector in second vector.
2082    ///
2083    /// Like R's `match()` function.
2084    ///
2085    /// # Parameters
2086    ///
2087    /// - `x`: Vector of values to match
2088    /// - `table`: Vector to match against
2089    /// - `nomatch`: Value to return for non-matches
2090    ///
2091    /// # Returns
2092    ///
2093    /// Integer vector of match positions (1-indexed, nomatch for non-matches).
2094    #[doc(alias = "match")]
2095    pub fn Rf_match(x: SEXP, table: SEXP, nomatch: ::std::os::raw::c_int) -> SEXP;
2096
2097    // Duplication and copying
2098
2099    /// Copy most attributes from source to target.
2100    ///
2101    /// Copies all attributes except names, dim, and dimnames.
2102    #[doc(alias = "copyMostAttrib")]
2103    pub fn Rf_copyMostAttrib(source: SEXP, target: SEXP);
2104
2105    /// Find first duplicated element.
2106    ///
2107    /// # Parameters
2108    ///
2109    /// - `x`: Vector to search
2110    /// - `fromLast`: If TRUE, search from end
2111    ///
2112    /// # Returns
2113    ///
2114    /// 0 if no duplicates, otherwise 1-indexed position of first duplicate.
2115    #[doc(alias = "any_duplicated")]
2116    pub fn Rf_any_duplicated(x: SEXP, fromLast: Rboolean) -> R_xlen_t;
2117
2118    // S4 functions
2119
2120    /// Convert to an S4 object.
2121    ///
2122    /// # Parameters
2123    ///
2124    /// - `object`: Object to convert
2125    /// - `flag`: Conversion flag
2126    #[doc(alias = "asS4")]
2127    pub fn Rf_asS4(object: SEXP, flag: Rboolean, complete: ::std::os::raw::c_int) -> SEXP;
2128
2129    /// Get the S3 class of an S4 object.
2130    #[doc(alias = "S3Class")]
2131    pub fn Rf_S3Class(object: SEXP) -> SEXP;
2132
2133    // Option access
2134
2135    /// Get an R option value.
2136    ///
2137    /// Equivalent to `getOption("name")` in R.
2138    ///
2139    /// # Parameters
2140    ///
2141    /// - `tag`: Symbol for option name
2142    #[doc(alias = "GetOption1")]
2143    pub fn Rf_GetOption1(tag: SEXP) -> SEXP;
2144
2145    /// Get the `digits` option.
2146    ///
2147    /// Returns the value of `getOption("digits")`.
2148    #[doc(alias = "GetOptionDigits")]
2149    pub fn Rf_GetOptionDigits() -> ::std::os::raw::c_int;
2150
2151    /// Get the `width` option.
2152    ///
2153    /// Returns the value of `getOption("width")`.
2154    #[doc(alias = "GetOptionWidth")]
2155    pub(crate) fn Rf_GetOptionWidth() -> ::std::os::raw::c_int;
2156
2157    // Factor functions
2158
2159    /// Check if a factor is ordered.
2160    #[doc(alias = "isOrdered")]
2161    pub fn Rf_isOrdered(s: SEXP) -> Rboolean;
2162
2163    /// Check if a factor is unordered.
2164    #[doc(alias = "isUnordered")]
2165    pub fn Rf_isUnordered(s: SEXP) -> Rboolean;
2166
2167    /// Check if a vector is unsorted.
2168    ///
2169    /// # Parameters
2170    ///
2171    /// - `x`: Vector to check
2172    /// - `strictly`: If TRUE, check for strictly increasing
2173    #[doc(alias = "isUnsorted")]
2174    pub fn Rf_isUnsorted(x: SEXP, strictly: Rboolean) -> ::std::os::raw::c_int;
2175
2176    // Expression and evaluation
2177
2178    /// Substitute in an expression.
2179    ///
2180    /// Like R's `substitute()` function.
2181    #[doc(alias = "substitute")]
2182    pub fn Rf_substitute(lang: SEXP, rho: SEXP) -> SEXP;
2183
2184    /// Set vector length.
2185    ///
2186    /// For short vectors (length < 2^31).
2187    #[doc(alias = "lengthgets")]
2188    pub fn Rf_lengthgets(x: SEXP, newlen: R_xlen_t) -> SEXP;
2189
2190    /// Set vector length (long vector version).
2191    #[doc(alias = "xlengthgets")]
2192    pub fn Rf_xlengthgets(x: SEXP, newlen: R_xlen_t) -> SEXP;
2193
2194    // Protection (indexed — see cost table in the "GC protection" region above)
2195
2196    /// Protect a SEXP and record its stack index for later `R_Reprotect`.
2197    ///
2198    /// **Cost: O(1)** — same array write as `Rf_protect`, plus stores the index.
2199    /// No allocation. Use when you need to replace a protected value in-place
2200    /// (e.g., inside a loop that allocates) without unprotect/re-protect churn.
2201    #[doc(alias = "PROTECT_WITH_INDEX")]
2202    pub fn R_ProtectWithIndex(s: SEXP, index: *mut ::std::os::raw::c_int);
2203
2204    /// Replace the SEXP at a previously recorded protect stack index.
2205    ///
2206    /// **Cost: O(1)** — direct array write (`R_PPStack[index] = s`). No allocation.
2207    ///
2208    /// # Safety
2209    ///
2210    /// `index` must be from a previous `R_ProtectWithIndex` call and the
2211    /// stack must not have been unprotected past that index.
2212    #[doc(alias = "REPROTECT")]
2213    pub fn R_Reprotect(s: SEXP, index: ::std::os::raw::c_int);
2214
2215    // Weak references
2216
2217    /// Create a weak reference.
2218    ///
2219    /// # Parameters
2220    ///
2221    /// - `key`: The key object (weak reference target)
2222    /// - `val`: The value to associate
2223    /// - `fin`: Finalizer function (or R_NilValue)
2224    /// - `onexit`: Whether to run finalizer on R exit
2225    pub fn R_MakeWeakRef(key: SEXP, val: SEXP, fin: SEXP, onexit: Rboolean) -> SEXP;
2226
2227    /// Create a weak reference with C finalizer.
2228    pub fn R_MakeWeakRefC(key: SEXP, val: SEXP, fin: R_CFinalizer_t, onexit: Rboolean) -> SEXP;
2229
2230    /// Get the key from a weak reference.
2231    pub fn R_WeakRefKey(w: SEXP) -> SEXP;
2232
2233    /// Get the value from a weak reference.
2234    pub fn R_WeakRefValue(w: SEXP) -> SEXP;
2235
2236    /// Run pending finalizers.
2237    pub fn R_RunPendingFinalizers();
2238
2239    // Conversion list/vector
2240
2241    /// Convert a pairlist to a generic vector (list).
2242    #[doc(alias = "PairToVectorList")]
2243    pub fn Rf_PairToVectorList(x: SEXP) -> SEXP;
2244
2245    /// Convert a generic vector (list) to a pairlist.
2246    #[doc(alias = "VectorToPairList")]
2247    pub fn Rf_VectorToPairList(x: SEXP) -> SEXP;
2248
2249    // Install with CHARSXP
2250
2251    /// Install a symbol from a CHARSXP.
2252    ///
2253    /// Like `Rf_install()` but takes a CHARSXP instead of C string.
2254    #[doc(alias = "installChar")]
2255    pub fn Rf_installChar(x: SEXP) -> SEXP;
2256}
2257
2258// endregion