Skip to main content

miniextendr_api/
sexp_types.rs

1//! Core type vocabulary for R values: `SEXPTYPE`, `R_xlen_t`, `Rbyte`,
2//! `Rcomplex`, `RLogical`, `Rboolean`, `cetype_t`, `R_CFinalizer_t`, and
3//! the `RNativeType` marker trait + impls.
4//!
5//! These types are the bridge between raw R FFI (in `crate::sys`) and the
6//! safe Rust API on `SEXP` (in `crate::sexp` and `crate::sexp_ext`).
7//! Most user code reaches them via [`crate::prelude`].
8
9use crate::SEXP;
10use crate::altrep_traits::NA_REAL;
11use crate::sys::{
12    COMPLEX, COMPLEX_ELT, INTEGER, INTEGER_ELT, LOGICAL, LOGICAL_ELT, RAW, RAW_ELT, REAL, REAL_ELT,
13    Rf_type2char, Rf_xlength,
14};
15
16#[allow(non_camel_case_types)]
17/// R's extended vector length type (`R_xlen_t`).
18pub type R_xlen_t = isize;
19/// R byte element type used by `RAWSXP`.
20pub type Rbyte = ::std::os::raw::c_uchar;
21
22/// R's complex scalar layout (`Rcomplex`).
23#[repr(C)]
24#[derive(Debug, Copy, Clone, PartialEq)]
25pub struct Rcomplex {
26    /// Real part.
27    pub r: f64,
28    /// Imaginary part.
29    pub i: f64,
30}
31
32/// R S-expression tag values (`SEXPTYPE`).
33#[repr(u32)]
34#[non_exhaustive]
35#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
36pub enum SEXPTYPE {
37    #[doc = " nil = NULL"]
38    NILSXP = 0,
39    #[doc = " symbols"]
40    SYMSXP = 1,
41    #[doc = " lists of dotted pairs"]
42    LISTSXP = 2,
43    #[doc = " closures"]
44    CLOSXP = 3,
45    #[doc = " environments"]
46    ENVSXP = 4,
47    #[doc = r" promises: \[un\]evaluated closure arguments"]
48    PROMSXP = 5,
49    #[doc = " language constructs (special lists)"]
50    LANGSXP = 6,
51    #[doc = " special forms"]
52    SPECIALSXP = 7,
53    #[doc = " builtin non-special forms"]
54    BUILTINSXP = 8,
55    #[doc = " \"scalar\" string type (internal only)"]
56    CHARSXP = 9,
57    #[doc = " logical vectors"]
58    LGLSXP = 10,
59    #[doc = " integer vectors"]
60    INTSXP = 13,
61    #[doc = " real variables"]
62    REALSXP = 14,
63    #[doc = " complex variables"]
64    CPLXSXP = 15,
65    #[doc = " string vectors"]
66    STRSXP = 16,
67    #[doc = " dot-dot-dot object"]
68    DOTSXP = 17,
69    #[doc = " make \"any\" args work"]
70    ANYSXP = 18,
71    #[doc = " generic vectors"]
72    VECSXP = 19,
73    #[doc = " expressions vectors"]
74    EXPRSXP = 20,
75    #[doc = " byte code"]
76    BCODESXP = 21,
77    #[doc = " external pointer"]
78    EXTPTRSXP = 22,
79    #[doc = " weak reference"]
80    WEAKREFSXP = 23,
81    #[doc = " raw bytes"]
82    RAWSXP = 24,
83    #[doc = " S4 non-vector"]
84    S4SXP = 25,
85    #[doc = " fresh node created in new page"]
86    NEWSXP = 30,
87    #[doc = " node released by GC"]
88    FREESXP = 31,
89    #[doc = " Closure or Builtin"]
90    FUNSXP = 99,
91}
92
93impl SEXPTYPE {
94    /// Alias for `S4SXP` (value 25).
95    ///
96    /// R defines both `OBJSXP` and `S4SXP` as value 25. `S4SXP` is retained
97    /// for backwards compatibility; `OBJSXP` is the preferred name.
98    pub const OBJSXP: SEXPTYPE = SEXPTYPE::S4SXP;
99
100    /// Get R's name for this SEXPTYPE (e.g. `"double"`, `"integer"`, `"list"`).
101    ///
102    /// Returns the same string as R's `typeof()` function.
103    #[inline]
104    pub fn type_name(self) -> &'static str {
105        let cstr = unsafe { Rf_type2char(self) };
106        // SAFETY: R's type names are static ASCII strings
107        unsafe { std::ffi::CStr::from_ptr(cstr) }
108            .to_str()
109            .unwrap_or("unknown")
110    }
111}
112
113/// Marker trait for types that correspond to R's native vector element types.
114///
115/// This enables blanket implementations for `TryFromSexp` and safe conversions.
116pub trait RNativeType: Sized + Copy + 'static {
117    /// The SEXPTYPE for vectors containing this element type.
118    const SEXP_TYPE: SEXPTYPE;
119
120    /// The per-type `NA` (missing-value) sentinel used when filling vector slots
121    /// that have no source value (e.g. sparse scatter into a longer column).
122    ///
123    /// - `f64`   → `NA_REAL` (R's canonical NA double bit pattern, *not* a plain `NaN`)
124    /// - `i32`   → `i32::MIN` (`NA_INTEGER`)
125    /// - `RLogical` → `RLogical::NA` (`NA_LOGICAL`)
126    /// - `Rcomplex` → both parts `NA_REAL`
127    /// - `u8` (RAWSXP) → `0` — **R's raw type has no NA**, so absent positions
128    ///   become `0x00` rather than a missing marker.
129    const R_NA: Self;
130
131    /// Get mutable pointer to vector data.
132    ///
133    /// For empty vectors (length 0), returns an aligned dangling pointer rather than
134    /// R's internal 0x1 sentinel, which isn't properly aligned for most types.
135    /// This allows safe creation of zero-length slices with `std::slice::from_raw_parts_mut`.
136    ///
137    /// # Safety
138    ///
139    /// - `sexp` must be a valid, non-null SEXP of the corresponding vector type.
140    /// - For ALTREP vectors, this may trigger materialization.
141    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self;
142
143    /// Read the i-th element via the appropriate `*_ELT` accessor.
144    ///
145    /// Goes through R's ALTREP dispatch for ALTREP vectors.
146    fn elt(sexp: SEXP, i: isize) -> Self;
147}
148
149/// R's logical element type (the contents of a `LGLSXP` vector).
150///
151/// In R, logical vectors are stored as `int` with possible values:
152/// - `0` for FALSE
153/// - `1` for TRUE
154/// - `NA_LOGICAL` (typically `INT_MIN`) for NA
155///
156/// **Important:** R may also contain other non-zero values in logical vectors
157/// (e.g., from low-level code). Those should be interpreted as TRUE.
158///
159/// This type is `repr(transparent)` over `i32` so *any* raw value is valid,
160/// avoiding UB when viewing `LGLSXP` data as a slice.
161#[repr(transparent)]
162#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
163pub struct RLogical(i32);
164
165impl RLogical {
166    /// FALSE logical scalar.
167    pub const FALSE: Self = Self(0);
168    /// TRUE logical scalar.
169    pub const TRUE: Self = Self(1);
170    /// Missing logical scalar (`NA_LOGICAL`).
171    pub const NA: Self = Self(i32::MIN);
172
173    /// Construct directly from raw R logical storage.
174    #[inline]
175    pub const fn from_i32(raw: i32) -> Self {
176        Self(raw)
177    }
178
179    /// Get raw R logical storage value.
180    #[inline]
181    pub const fn to_i32(self) -> i32 {
182        self.0
183    }
184
185    /// Returns whether the value is `NA_LOGICAL`.
186    #[inline]
187    pub const fn is_na(self) -> bool {
188        self.0 == i32::MIN
189    }
190
191    /// Convert to Rust `Option<bool>` (`None` for `NA`).
192    #[inline]
193    pub const fn to_option_bool(self) -> Option<bool> {
194        match self.0 {
195            0 => Some(false),
196            i32::MIN => None,
197            _ => Some(true),
198        }
199    }
200}
201
202impl From<bool> for RLogical {
203    #[inline]
204    fn from(value: bool) -> Self {
205        if value { Self::TRUE } else { Self::FALSE }
206    }
207}
208
209impl RNativeType for i32 {
210    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::INTSXP;
211    const R_NA: Self = i32::MIN; // NA_INTEGER
212
213    #[inline]
214    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
215        unsafe {
216            if Rf_xlength(sexp) == 0 {
217                std::ptr::NonNull::<Self>::dangling().as_ptr()
218            } else {
219                INTEGER(sexp)
220            }
221        }
222    }
223
224    #[inline]
225    fn elt(sexp: SEXP, i: isize) -> Self {
226        unsafe { INTEGER_ELT(sexp, i) }
227    }
228}
229
230impl RNativeType for f64 {
231    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::REALSXP;
232    const R_NA: Self = NA_REAL; // R's canonical NA double bit pattern, not f64::NAN
233
234    #[inline]
235    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
236        unsafe {
237            if Rf_xlength(sexp) == 0 {
238                std::ptr::NonNull::<Self>::dangling().as_ptr()
239            } else {
240                REAL(sexp)
241            }
242        }
243    }
244
245    #[inline]
246    fn elt(sexp: SEXP, i: isize) -> Self {
247        unsafe { REAL_ELT(sexp, i) }
248    }
249}
250
251impl RNativeType for u8 {
252    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::RAWSXP;
253    // R's raw vectors have no NA; absent scatter positions become 0x00.
254    const R_NA: Self = 0;
255
256    #[inline]
257    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
258        unsafe {
259            if Rf_xlength(sexp) == 0 {
260                std::ptr::NonNull::<Self>::dangling().as_ptr()
261            } else {
262                RAW(sexp)
263            }
264        }
265    }
266
267    #[inline]
268    fn elt(sexp: SEXP, i: isize) -> Self {
269        unsafe { RAW_ELT(sexp, i) }
270    }
271}
272
273impl RNativeType for RLogical {
274    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::LGLSXP;
275    const R_NA: Self = RLogical::NA; // NA_LOGICAL
276
277    #[inline]
278    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
279        // LOGICAL returns *mut c_int, RLogical is repr(transparent) over i32
280        unsafe {
281            if Rf_xlength(sexp) == 0 {
282                std::ptr::NonNull::<Self>::dangling().as_ptr()
283            } else {
284                LOGICAL(sexp).cast()
285            }
286        }
287    }
288
289    #[inline]
290    fn elt(sexp: SEXP, i: isize) -> Self {
291        RLogical(unsafe { LOGICAL_ELT(sexp, i) })
292    }
293}
294
295impl RNativeType for Rcomplex {
296    const SEXP_TYPE: SEXPTYPE = SEXPTYPE::CPLXSXP;
297    const R_NA: Self = Rcomplex {
298        r: NA_REAL,
299        i: NA_REAL,
300    };
301
302    #[inline]
303    unsafe fn dataptr_mut(sexp: SEXP) -> *mut Self {
304        unsafe {
305            if Rf_xlength(sexp) == 0 {
306                std::ptr::NonNull::<Self>::dangling().as_ptr()
307            } else {
308                COMPLEX(sexp)
309            }
310        }
311    }
312
313    #[inline]
314    fn elt(sexp: SEXP, i: isize) -> Self {
315        unsafe { COMPLEX_ELT(sexp, i) }
316    }
317}
318
319#[repr(i32)]
320#[non_exhaustive]
321#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
322/// Binary boolean used by many R C APIs.
323pub enum Rboolean {
324    /// False.
325    FALSE = 0,
326    /// True.
327    TRUE = 1,
328}
329
330impl From<bool> for Rboolean {
331    fn from(value: bool) -> Self {
332        match value {
333            true => Rboolean::TRUE,
334            false => Rboolean::FALSE,
335        }
336    }
337}
338
339impl From<Rboolean> for bool {
340    fn from(value: Rboolean) -> Self {
341        match value {
342            Rboolean::FALSE => false,
343            Rboolean::TRUE => true,
344        }
345    }
346}
347
348#[allow(non_camel_case_types)]
349/// C finalizer callback signature used by external pointers.
350pub type R_CFinalizer_t = ::std::option::Option<unsafe extern "C-unwind" fn(s: SEXP)>;
351
352#[repr(C)]
353#[derive(Copy, Clone)]
354#[allow(non_camel_case_types)]
355/// Character encoding tag used by CHARSXP constructors.
356pub enum cetype_t {
357    /// Native locale encoding.
358    CE_NATIVE = 0,
359    /// UTF-8 encoding.
360    CE_UTF8 = 1,
361    /// Latin-1 encoding.
362    CE_LATIN1 = 2,
363    /// Raw bytes encoding.
364    CE_BYTES = 3,
365    /// Symbol encoding marker.
366    CE_SYMBOL = 5,
367    /// Any encoding accepted.
368    CE_ANY = 99,
369}
370pub use cetype_t::CE_UTF8;