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