Skip to main content

rd_rds/
value.rs

1use std::{borrow::Cow, sync::Arc};
2
3use crate::Error;
4
5#[derive(Debug, Clone, PartialEq)]
6#[non_exhaustive]
7pub struct RObject {
8    value: RValue,
9    attributes: Attributes,
10}
11
12#[derive(Debug, Clone, PartialEq)]
13#[non_exhaustive]
14pub enum RValue {
15    Null,
16    Logical(Vec<Option<bool>>),
17    Integer(Vec<Option<i32>>),
18    Real(Vec<Option<f64>>),
19    Character(Vec<RStr>),
20    List(Vec<RObject>),
21    Symbol(Symbol),
22    Persisted(Persisted),
23    Environment(EnvHandle),
24}
25
26/// An opaque handle to an environment.
27///
28/// `rd-rds` never models environment frames or enclosures: it only consumes
29/// the wire bytes correctly and keeps the reference table aligned. All
30/// non-singleton environments collapse to [`EnvHandle::Other`].
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32#[non_exhaustive]
33pub enum EnvHandle {
34    /// `R_GlobalEnv`.
35    Global,
36    /// `R_BaseEnv` / `R_BaseNamespace`.
37    Base,
38    /// `R_EmptyEnv`.
39    Empty,
40    /// Any other environment/package/namespace. Its wire fields were
41    /// decoded (to keep the stream and reference table in sync) and then
42    /// discarded.
43    Other,
44}
45
46#[derive(Debug, Clone, PartialEq)]
47pub struct Attribute {
48    name: Symbol,
49    value: RObject,
50}
51
52impl Attribute {
53    pub fn new(name: Symbol, value: RObject) -> Self {
54        Self { name, value }
55    }
56
57    pub fn name(&self) -> &Symbol {
58        &self.name
59    }
60
61    pub fn value(&self) -> &RObject {
62        &self.value
63    }
64}
65
66#[derive(Debug, Clone, PartialEq, Default)]
67pub struct Attributes(Vec<Attribute>);
68
69impl Attributes {
70    /// Constructs an attribute collection from decoded attributes.
71    pub fn new(values: Vec<Attribute>) -> Self {
72        Self(values)
73    }
74
75    pub fn is_empty(&self) -> bool {
76        self.0.is_empty()
77    }
78
79    pub fn len(&self) -> usize {
80        self.0.len()
81    }
82
83    pub fn iter(&self) -> impl Iterator<Item = &Attribute> {
84        self.0.iter()
85    }
86
87    pub fn get(&self, name: &str) -> Option<&RObject> {
88        self.0
89            .iter()
90            .find(|attribute| attribute.name().as_str() == name)
91            .map(|attribute| attribute.value())
92    }
93}
94
95impl RObject {
96    pub fn from_parts(value: RValue, attributes: Attributes) -> Self {
97        Self { value, attributes }
98    }
99
100    pub fn value(&self) -> &RValue {
101        &self.value
102    }
103
104    pub fn attributes(&self) -> &Attributes {
105        &self.attributes
106    }
107
108    pub fn into_parts(self) -> (RValue, Attributes) {
109        (self.value, self.attributes)
110    }
111
112    pub fn class(&self) -> Option<&[RStr]> {
113        match self.attributes.get("class")?.value() {
114            RValue::Character(values) => Some(values),
115            _ => None,
116        }
117    }
118
119    pub fn names(&self) -> Option<&[RStr]> {
120        match self.attributes.get("names")?.value() {
121            RValue::Character(values) => Some(values),
122            _ => None,
123        }
124    }
125
126    pub fn get_named(&self, name: &str) -> Option<&RObject> {
127        let RValue::List(items) = self.value() else {
128            return None;
129        };
130        let names = self.names()?;
131        names
132            .iter()
133            .position(|value| value.as_str().and_then(Result::ok).as_deref() == Some(name))
134            .and_then(|index| items.get(index))
135    }
136}
137
138/// A symbol whose print name is decoded eagerly during parsing, so it always
139/// holds valid text.
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub struct Symbol(Arc<str>);
142
143impl Symbol {
144    pub fn new(value: impl Into<Arc<str>>) -> Self {
145        Self(value.into())
146    }
147
148    pub fn as_str(&self) -> &str {
149        &self.0
150    }
151}
152
153impl From<&str> for Symbol {
154    fn from(value: &str) -> Self {
155        Self::new(value)
156    }
157}
158
159#[derive(Debug, Clone, PartialEq)]
160pub struct Persisted(Arc<[RStr]>);
161
162impl Persisted {
163    pub(crate) fn new(values: Vec<RStr>) -> Self {
164        Self(values.into())
165    }
166
167    #[cfg(test)]
168    pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
169        Arc::ptr_eq(&self.0, &other.0)
170    }
171
172    pub fn as_slice(&self) -> &[RStr] {
173        &self.0
174    }
175}
176
177#[derive(Debug, Clone, PartialEq, Eq)]
178#[non_exhaustive]
179pub enum RStr {
180    Na,
181    Value(RStrValue),
182}
183
184/// The source of native-encoding context used to interpret an R string.
185#[derive(Debug, Clone, PartialEq, Eq)]
186#[non_exhaustive]
187pub enum NativeEncodingSource {
188    /// The stream header declared this native encoding name (format 3 only).
189    Header(Arc<str>),
190    /// The header carried no native encoding and the caller opted into
191    /// [`crate::NativeEncodingPolicy::AssumeUtf8`].
192    AssumedUtf8,
193    /// The header carried no native encoding and the caller made no
194    /// assumption.
195    Unknown,
196}
197
198impl NativeEncodingSource {
199    pub(crate) fn decodes_as_utf8(&self) -> bool {
200        match self {
201            Self::Header(name) => is_utf8_native_encoding(Some(name)),
202            Self::AssumedUtf8 => true,
203            Self::Unknown => false,
204        }
205    }
206}
207
208/// The opaque payload of an [`RStr::Value`] string.
209#[derive(Debug, Clone, PartialEq, Eq)]
210pub struct RStrValue {
211    bytes: Arc<[u8]>,
212    encoding: REncoding,
213    native_encoding_source: NativeEncodingSource,
214}
215
216impl RStr {
217    /// Constructs an R string value from its serialized bytes and encoding
218    /// context.
219    pub fn new(
220        bytes: &[u8],
221        encoding: REncoding,
222        native_encoding_source: NativeEncodingSource,
223    ) -> Self {
224        Self::Value(RStrValue {
225            bytes: Arc::from(bytes),
226            encoding,
227            native_encoding_source,
228        })
229    }
230
231    pub fn as_str(&self) -> Option<Result<Cow<'_, str>, Error>> {
232        match self {
233            Self::Na => None,
234            Self::Value(value) => Some(value.as_str()),
235        }
236    }
237
238    /// Reports the CHARSXP encoding flag recorded in the serialized data.
239    /// R's `Encoding()` normalizes ASCII strings to "unknown" after reading,
240    /// so the two can differ while the decoded string contents agree.
241    pub fn encoding(&self) -> Option<REncoding> {
242        match self {
243            Self::Na => None,
244            Self::Value(value) => Some(value.encoding()),
245        }
246    }
247}
248
249impl RStrValue {
250    fn as_str(&self) -> Result<Cow<'_, str>, Error> {
251        match self.encoding {
252            REncoding::Native => {
253                if self.bytes.is_ascii() || self.native_encoding_source.decodes_as_utf8() {
254                    std::str::from_utf8(&self.bytes)
255                        .map(Cow::Borrowed)
256                        .map_err(|_| Error::InvalidStringEncoding)
257                } else {
258                    Err(Error::InvalidStringEncoding)
259                }
260            }
261            REncoding::Utf8 => std::str::from_utf8(&self.bytes)
262                .map(Cow::Borrowed)
263                .map_err(|_| Error::InvalidStringEncoding),
264            REncoding::Latin1 => Ok(Cow::Owned(
265                self.bytes.iter().map(|&byte| byte as char).collect(),
266            )),
267            REncoding::Bytes => Err(Error::InvalidStringEncoding),
268        }
269    }
270
271    /// Returns the serialized string bytes without converting them.
272    pub fn bytes(&self) -> &[u8] {
273        &self.bytes
274    }
275
276    /// Returns the CHARSXP encoding flag recorded in the serialized data.
277    pub fn encoding(&self) -> REncoding {
278        self.encoding
279    }
280
281    /// Returns the native-encoding provenance used for this value.
282    pub fn native_encoding_source(&self) -> &NativeEncodingSource {
283        &self.native_encoding_source
284    }
285
286    /// Returns the native encoding name declared by the stream header, if any.
287    ///
288    /// This reports header evidence only; it is not the effective decoding
289    /// context and therefore returns `None` for [`NativeEncodingSource::AssumedUtf8`]
290    /// and [`NativeEncodingSource::Unknown`].
291    pub fn header_native_encoding(&self) -> Option<&str> {
292        match &self.native_encoding_source {
293            NativeEncodingSource::Header(name) => Some(name),
294            NativeEncodingSource::AssumedUtf8 | NativeEncodingSource::Unknown => None,
295        }
296    }
297}
298
299fn is_utf8_native_encoding(encoding: Option<&str>) -> bool {
300    encoding
301        .map(|encoding| {
302            encoding.eq_ignore_ascii_case("UTF-8") || encoding.eq_ignore_ascii_case("UTF8")
303        })
304        .unwrap_or(false)
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Eq)]
308#[non_exhaustive]
309pub enum REncoding {
310    Native,
311    Utf8,
312    Latin1,
313    Bytes,
314}
315
316#[cfg(test)]
317mod tests {
318    use super::*;
319
320    #[test]
321    fn native_ascii_strings_decode_without_header_encoding() {
322        let value = RStr::new(b"name", REncoding::Native, NativeEncodingSource::Unknown);
323        assert_eq!(value.as_str().unwrap().unwrap().as_ref(), "name");
324    }
325
326    #[test]
327    fn native_utf8_strings_decode_when_header_encoding_is_utf8() {
328        let value = RStr::new(
329            "cafe\u{301}".as_bytes(),
330            REncoding::Native,
331            NativeEncodingSource::Header(Arc::from("UTF-8")),
332        );
333        assert_eq!(value.as_str().unwrap().unwrap().as_ref(), "cafe\u{301}");
334    }
335
336    #[test]
337    fn native_non_ascii_strings_reject_unknown_native_encoding() {
338        let value = RStr::new(
339            "é".as_bytes(),
340            REncoding::Native,
341            NativeEncodingSource::Unknown,
342        );
343        assert_eq!(
344            value.as_str().unwrap().unwrap_err(),
345            Error::InvalidStringEncoding
346        );
347    }
348}
349
350#[derive(Debug, Clone, Copy)]
351pub struct Limits {
352    max_depth: u32,
353    max_vector_len: usize,
354    max_total_elements: usize,
355}
356
357impl Default for Limits {
358    fn default() -> Self {
359        Self {
360            max_depth: 5_000,
361            max_vector_len: 8_000_000,
362            max_total_elements: 16_000_000,
363        }
364    }
365}
366
367impl Limits {
368    pub fn max_depth(mut self, value: u32) -> Self {
369        self.max_depth = value;
370        self
371    }
372
373    pub fn max_vector_len(mut self, value: usize) -> Self {
374        self.max_vector_len = value;
375        self
376    }
377
378    pub fn max_total_elements(mut self, value: usize) -> Self {
379        self.max_total_elements = value;
380        self
381    }
382
383    pub(crate) fn max_depth_value(self) -> u32 {
384        self.max_depth
385    }
386    pub(crate) fn max_vector_len_value(self) -> usize {
387        self.max_vector_len
388    }
389    pub(crate) fn max_total_elements_value(self) -> usize {
390        self.max_total_elements
391    }
392}
393
394#[derive(Debug, Clone, Copy, PartialEq, Eq)]
395#[non_exhaustive]
396pub enum SexpKind {
397    Nil,
398    Sym,
399    PairList,
400    Env,
401    Char,
402    Logical,
403    Integer,
404    Real,
405    String,
406    List,
407    ExtPtr,
408    WeakRef,
409    Persist,
410    Package,
411    Namespace,
412    Ref,
413    Closure,
414    BuiltIn,
415    Special,
416    Promise,
417    Lang,
418    DotDotDot,
419    ByteCode,
420    S4,
421    Complex,
422    Raw,
423    Other(u8),
424}
425
426impl SexpKind {
427    pub fn from_type_code(type_code: u8) -> Self {
428        match type_code {
429            0 | 254 => Self::Nil,
430            1 => Self::Sym,
431            2 => Self::PairList,
432            3 => Self::Closure,
433            4 => Self::Env,
434            5 => Self::Promise,
435            6 => Self::Lang,
436            7 => Self::Special,
437            8 => Self::BuiltIn,
438            9 => Self::Char,
439            10 => Self::Logical,
440            13 => Self::Integer,
441            14 => Self::Real,
442            15 => Self::Complex,
443            16 => Self::String,
444            17 => Self::DotDotDot,
445            19 => Self::List,
446            21 => Self::ByteCode,
447            22 => Self::ExtPtr,
448            23 => Self::WeakRef,
449            24 => Self::Raw,
450            25 => Self::S4,
451            247 => Self::Persist,
452            248 => Self::Package,
453            249 | 250 => Self::Namespace,
454            255 => Self::Ref,
455            other => Self::Other(other),
456        }
457    }
458}