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