Skip to main content

rd_rds/
decode.rs

1use std::sync::Arc;
2
3use crate::{
4    Attribute, Attributes, ByteCursor, EnvHandle, Error, Header, Limits, Persisted, REncoding,
5    RObject, RStr, RValue, SexpKind, Symbol,
6};
7
8const TYPE_MASK: u32 = 0xff;
9const ATTRIBUTES_BIT: u32 = 1 << 9;
10const TAG_BIT: u32 = 1 << 10;
11const LEVELS_SHIFT: u32 = 12;
12
13const NILSXP: u8 = 0;
14const SYMSXP: u8 = 1;
15const LISTSXP: u8 = 2;
16const CLOSXP: u8 = 3;
17const ENVSXP: u8 = 4;
18const PROMSXP: u8 = 5;
19const LANGSXP: u8 = 6;
20const SPECIALSXP: u8 = 7;
21const BUILTINSXP: u8 = 8;
22const CHARSXP: u8 = 9;
23const LGLSXP: u8 = 10;
24const INTSXP: u8 = 13;
25const REALSXP: u8 = 14;
26const CPLXSXP: u8 = 15;
27const STRSXP: u8 = 16;
28const DOTSXP: u8 = 17;
29const VECSXP: u8 = 19;
30const EXPRSXP: u8 = 20;
31const RAWSXP: u8 = 24;
32const S4SXP: u8 = 25;
33#[cfg(test)]
34const EXTPTRSXP: u8 = 22;
35const BASEENV_SXP: u8 = 241;
36const EMPTYENV_SXP: u8 = 242;
37const PACKAGESXP: u8 = 248;
38const NAMESPACESXP: u8 = 249;
39const BASENAMESPACE_SXP: u8 = 250;
40const MISSINGARG_SXP: u8 = 251;
41const UNBOUNDVALUE_SXP: u8 = 252;
42const GLOBALENV_SXP: u8 = 253;
43const NILVALUE_SXP: u8 = 254;
44const REFSXP: u8 = 255;
45const PERSISTSXP: u8 = 247;
46
47const NA_INTEGER: i32 = i32::MIN;
48const NA_REAL_BITS: u64 = 0x7ff0_0000_0000_07a2;
49
50pub fn parse(bytes: &[u8]) -> Result<RObject, Error> {
51    parse_with_limits(bytes, Limits::default())
52}
53
54pub fn parse_with_limits(bytes: &[u8], limits: Limits) -> Result<RObject, Error> {
55    let mut cursor = ByteCursor::new(bytes);
56    let header = Header::parse(&mut cursor)?;
57    Decoder::new(limits, header.native_encoding).decode_root(&mut cursor)
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub(crate) struct ItemFlags {
62    raw: u32,
63    type_code: u8,
64}
65
66impl ItemFlags {
67    pub(crate) fn from_raw(raw: u32) -> Self {
68        Self {
69            raw,
70            type_code: (raw & TYPE_MASK) as u8,
71        }
72    }
73
74    fn type_code(self) -> u8 {
75        self.type_code
76    }
77
78    fn kind(self) -> SexpKind {
79        SexpKind::from_type_code(self.type_code)
80    }
81
82    fn has_attributes(self) -> bool {
83        self.raw & ATTRIBUTES_BIT != 0
84    }
85
86    fn has_tag(self) -> bool {
87        self.raw & TAG_BIT != 0
88    }
89
90    fn levels(self) -> u32 {
91        self.raw >> LEVELS_SHIFT
92    }
93
94    fn ref_index_inline(self) -> u32 {
95        self.raw >> 8
96    }
97
98    #[cfg(test)]
99    fn is_object(self) -> bool {
100        self.raw & (1 << 8) != 0
101    }
102}
103
104#[derive(Debug, Clone)]
105enum RefEntry {
106    Symbol(Symbol),
107    Persisted(Persisted),
108    Env(EnvHandle),
109}
110
111/// Traversal mode for the core decoder.
112///
113/// `Strict` is the public entry point's behavior: any SEXP type outside the
114/// modeled [`RValue`] set is rejected. `Discard` is used only while walking
115/// the item fields of a non-singleton environment (`enclos`/`frame`/
116/// `hashtab`/`attrib`): it performs the same structural walk (with the same
117/// reference-table side effects) but additionally tolerates SEXP types whose
118/// layout is verified but not otherwise modeled, discarding their decoded
119/// value.
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121enum Mode {
122    Strict,
123    Discard,
124}
125
126#[derive(Default)]
127struct RefTable {
128    entries: Vec<RefEntry>,
129}
130
131impl RefTable {
132    fn register(&mut self, entry: RefEntry) {
133        self.entries.push(entry);
134    }
135
136    fn resolve(&self, index: u32, offset: usize) -> Result<&RefEntry, Error> {
137        if index == 0 {
138            return Err(Error::RefIndexOutOfRange {
139                index,
140                len: self.entries.len(),
141                offset,
142            });
143        }
144        self.entries
145            .get(index as usize - 1)
146            .ok_or(Error::RefIndexOutOfRange {
147                index,
148                len: self.entries.len(),
149                offset,
150            })
151    }
152}
153
154struct Decoder {
155    refs: RefTable,
156    limits: Limits,
157    total_elements: usize,
158    native_encoding: Option<Arc<str>>,
159}
160
161impl Decoder {
162    fn new(limits: Limits, native_encoding: Option<String>) -> Self {
163        Self {
164            refs: RefTable::default(),
165            limits,
166            total_elements: 0,
167            native_encoding: native_encoding.map(Arc::from),
168        }
169    }
170
171    fn decode_root(&mut self, cursor: &mut ByteCursor<'_>) -> Result<RObject, Error> {
172        self.decode_object(cursor, 0, Mode::Strict)
173    }
174
175    fn decode_object(
176        &mut self,
177        cursor: &mut ByteCursor<'_>,
178        depth: u32,
179        mode: Mode,
180    ) -> Result<RObject, Error> {
181        self.check_depth(depth)?;
182        let flags = self.read_flags(cursor)?;
183        self.decode_object_with_flags(cursor, flags, depth, mode)
184    }
185
186    fn decode_object_with_flags(
187        &mut self,
188        cursor: &mut ByteCursor<'_>,
189        flags: ItemFlags,
190        depth: u32,
191        mode: Mode,
192    ) -> Result<RObject, Error> {
193        // Types with wire layouts that don't fit the generic
194        // "value, then optionally-gated attributes" shape below (bare
195        // singleton tags, or an environment whose attrib field is
196        // unconditional rather than flag-gated) are dispatched here and
197        // return directly, regardless of mode.
198        match flags.type_code() {
199            REFSXP => return self.decode_ref(cursor, flags),
200            ENVSXP => return self.decode_env(cursor, depth),
201            GLOBALENV_SXP => return Ok(env_object(EnvHandle::Global)),
202            BASEENV_SXP | BASENAMESPACE_SXP => return Ok(env_object(EnvHandle::Base)),
203            EMPTYENV_SXP => return Ok(env_object(EnvHandle::Empty)),
204            _ => {}
205        }
206
207        let value = match flags.type_code() {
208            NILSXP | NILVALUE_SXP => RValue::Null,
209            CHARSXP => RValue::Character(vec![self.decode_char_with_flags(cursor, flags)?]),
210            STRSXP => RValue::Character(self.decode_character_vector(cursor)?),
211            LGLSXP => RValue::Logical(self.decode_logical_vector(cursor)?),
212            INTSXP => RValue::Integer(self.decode_integer_vector(cursor)?),
213            REALSXP => RValue::Real(self.decode_real_vector(cursor)?),
214            VECSXP => RValue::List(self.decode_list(cursor, depth, mode)?),
215            SYMSXP => RValue::Symbol(self.decode_symbol_with_flags(cursor, flags)?),
216            PERSISTSXP => RValue::Persisted(self.decode_persisted(cursor)?),
217            PACKAGESXP | NAMESPACESXP => {
218                RValue::Environment(self.decode_package_or_namespace(cursor)?)
219            }
220            other => {
221                if mode == Mode::Discard {
222                    return self.decode_discard(cursor, flags, depth);
223                }
224                return Err(Error::UnsupportedSexp {
225                    kind: SexpKind::from_type_code(other),
226                    type_code: other,
227                    offset: cursor.position().saturating_sub(4),
228                });
229            }
230        };
231
232        let attributes = if flags.has_attributes() {
233            self.decode_attributes(cursor, depth + 1, mode)?
234        } else {
235            Attributes::default()
236        };
237
238        Ok(RObject::from_parts(value, attributes))
239    }
240
241    /// Decodes a non-singleton `ENVSXP`: `locked` (raw i32, registered
242    /// immediately after), then `enclos`/`frame`/`hashtab`/`attrib`, all
243    /// unconditionally present and all decoded (and discarded) in
244    /// [`Mode::Discard`]. Environments are opaque by design: only their
245    /// wire bytes and reference-table side effects matter.
246    fn decode_env(&mut self, cursor: &mut ByteCursor<'_>, depth: u32) -> Result<RObject, Error> {
247        let _locked = cursor.read_be_i32()?;
248        self.refs.register(RefEntry::Env(EnvHandle::Other));
249        for _ in 0..4 {
250            let _ = self.decode_object(cursor, depth + 1, Mode::Discard)?;
251        }
252        Ok(env_object(EnvHandle::Other))
253    }
254
255    /// Decodes the shared `PACKAGESXP`/`NAMESPACESXP` payload: the same
256    /// "string vec" format used by `PERSISTSXP`, registered in the
257    /// reference table after the payload is read.
258    fn decode_package_or_namespace(
259        &mut self,
260        cursor: &mut ByteCursor<'_>,
261    ) -> Result<EnvHandle, Error> {
262        let _ = self.decode_string_vec(cursor)?;
263        self.refs.register(RefEntry::Env(EnvHandle::Other));
264        Ok(EnvHandle::Other)
265    }
266
267    /// Handles the SEXP types that are only tolerated in [`Mode::Discard`]:
268    /// their wire layout is verified but they have no [`RValue`]
269    /// representation, so the decoded value is always discarded in favor of
270    /// [`RValue::Null`]. Types whose layout is not verified still fail with
271    /// [`Error::UnsupportedSexp`].
272    fn decode_discard(
273        &mut self,
274        cursor: &mut ByteCursor<'_>,
275        flags: ItemFlags,
276        depth: u32,
277    ) -> Result<RObject, Error> {
278        if is_dotted_pair(flags) {
279            // Dotted pairs handle their own (optional) attributes and tag
280            // internally, so they never fall through to the generic
281            // trailing-attributes handling below.
282            self.discard_pairlist_chain(cursor, flags, depth)?;
283            return Ok(RObject::from_parts(RValue::Null, Attributes::default()));
284        }
285
286        match flags.type_code() {
287            UNBOUNDVALUE_SXP | MISSINGARG_SXP => {
288                return Ok(RObject::from_parts(RValue::Null, Attributes::default()));
289            }
290            SPECIALSXP | BUILTINSXP => {
291                let len = self.read_vector_len(cursor)?;
292                let _ = cursor.read_exact(len)?;
293            }
294            CPLXSXP => {
295                let len = self.read_vector_len(cursor)?;
296                for _ in 0..len {
297                    let _ = cursor.read_exact(16)?;
298                }
299            }
300            RAWSXP => {
301                let len = self.read_vector_len(cursor)?;
302                let _ = cursor.read_exact(len)?;
303            }
304            EXPRSXP => {
305                // Same framing as VECSXP: a length followed by that many items.
306                let _ = self.decode_list(cursor, depth, Mode::Discard)?;
307            }
308            S4SXP => {
309                // No body content beyond the generic trailing attributes.
310            }
311            other => {
312                return Err(Error::UnsupportedSexp {
313                    kind: SexpKind::from_type_code(other),
314                    type_code: other,
315                    offset: cursor.position().saturating_sub(4),
316                });
317            }
318        }
319
320        let attributes = if flags.has_attributes() {
321            self.decode_attributes(cursor, depth + 1, Mode::Discard)?
322        } else {
323            Attributes::default()
324        };
325
326        Ok(RObject::from_parts(RValue::Null, attributes))
327    }
328
329    /// Discards a dotted-pair chain (`LISTSXP`/`LANGSXP`/`CLOSXP`/
330    /// `PROMSXP`/`DOTSXP`) iteratively over the CDR links, so long
331    /// pairlists don't add stack depth. Each link decodes an optional
332    /// attributes item, an optional tag item, and the CAR, all generically
333    /// in [`Mode::Discard`]; the CDR either continues the loop (another
334    /// dotted-pair link), stops (NIL), or is decoded once more as an
335    /// improper-list tail.
336    fn discard_pairlist_chain(
337        &mut self,
338        cursor: &mut ByteCursor<'_>,
339        flags: ItemFlags,
340        depth: u32,
341    ) -> Result<(), Error> {
342        let mut flags = flags;
343        loop {
344            self.account_elements(1, cursor.position())?;
345            if flags.has_attributes() {
346                let _ = self.decode_attributes(cursor, depth + 1, Mode::Discard)?;
347            }
348            if flags.has_tag() {
349                let _ = self.decode_object(cursor, depth + 1, Mode::Discard)?;
350            }
351            let _ = self.decode_object(cursor, depth + 1, Mode::Discard)?;
352
353            let cdr_flags = self.read_flags(cursor)?;
354            if is_dotted_pair(cdr_flags) {
355                flags = cdr_flags;
356                continue;
357            }
358            if is_nil(cdr_flags) {
359                return Ok(());
360            }
361            let _ = self.decode_object_with_flags(cursor, cdr_flags, depth + 1, Mode::Discard)?;
362            return Ok(());
363        }
364    }
365
366    fn read_flags(&mut self, cursor: &mut ByteCursor<'_>) -> Result<ItemFlags, Error> {
367        Ok(ItemFlags::from_raw(cursor.read_be_u32()?))
368    }
369
370    fn decode_ref(
371        &mut self,
372        cursor: &mut ByteCursor<'_>,
373        flags: ItemFlags,
374    ) -> Result<RObject, Error> {
375        let inline_index = flags.ref_index_inline();
376        let index = if inline_index == 0 {
377            cursor.read_be_i32()? as u32
378        } else {
379            inline_index
380        };
381
382        match self
383            .refs
384            .resolve(index, cursor.position().saturating_sub(4))?
385        {
386            RefEntry::Symbol(symbol) => Ok(RObject::from_parts(
387                RValue::Symbol(symbol.clone()),
388                Attributes::default(),
389            )),
390            RefEntry::Persisted(persisted) => Ok(RObject::from_parts(
391                RValue::Persisted(persisted.clone()),
392                Attributes::default(),
393            )),
394            RefEntry::Env(handle) => Ok(env_object(*handle)),
395        }
396    }
397
398    fn decode_list(
399        &mut self,
400        cursor: &mut ByteCursor<'_>,
401        depth: u32,
402        mode: Mode,
403    ) -> Result<Vec<RObject>, Error> {
404        let len = self.read_vector_len(cursor)?;
405        (0..len)
406            .map(|_| self.decode_object(cursor, depth + 1, mode))
407            .collect()
408    }
409
410    fn decode_logical_vector(
411        &mut self,
412        cursor: &mut ByteCursor<'_>,
413    ) -> Result<Vec<Option<bool>>, Error> {
414        let len = self.read_vector_len(cursor)?;
415        (0..len)
416            .map(|_| {
417                Ok(match cursor.read_be_i32()? {
418                    NA_INTEGER => None,
419                    0 => Some(false),
420                    _ => Some(true),
421                })
422            })
423            .collect()
424    }
425
426    fn decode_integer_vector(
427        &mut self,
428        cursor: &mut ByteCursor<'_>,
429    ) -> Result<Vec<Option<i32>>, Error> {
430        let len = self.read_vector_len(cursor)?;
431        (0..len)
432            .map(|_| {
433                let value = cursor.read_be_i32()?;
434                Ok((value != NA_INTEGER).then_some(value))
435            })
436            .collect()
437    }
438
439    fn decode_real_vector(
440        &mut self,
441        cursor: &mut ByteCursor<'_>,
442    ) -> Result<Vec<Option<f64>>, Error> {
443        let len = self.read_vector_len(cursor)?;
444        (0..len)
445            .map(|_| {
446                let bits = cursor.read_be_u64()?;
447                Ok((bits != NA_REAL_BITS).then_some(f64::from_bits(bits)))
448            })
449            .collect()
450    }
451
452    fn decode_character_vector(&mut self, cursor: &mut ByteCursor<'_>) -> Result<Vec<RStr>, Error> {
453        let len = self.read_vector_len(cursor)?;
454        (0..len).map(|_| self.decode_char_item(cursor)).collect()
455    }
456
457    fn decode_char_item(&mut self, cursor: &mut ByteCursor<'_>) -> Result<RStr, Error> {
458        let flags = self.read_flags(cursor)?;
459        if flags.type_code() != CHARSXP {
460            return Err(Error::UnsupportedSexp {
461                kind: flags.kind(),
462                type_code: flags.type_code(),
463                offset: cursor.position().saturating_sub(4),
464            });
465        }
466        self.decode_char_with_flags(cursor, flags)
467    }
468
469    fn decode_char_with_flags(
470        &mut self,
471        cursor: &mut ByteCursor<'_>,
472        flags: ItemFlags,
473    ) -> Result<RStr, Error> {
474        let len = cursor.read_be_i32()?;
475        if len == -1 {
476            return Ok(RStr::Na);
477        }
478        if len < 0 {
479            return Err(Error::NegativeLength {
480                len,
481                offset: cursor.position().saturating_sub(4),
482            });
483        }
484
485        let encoding = decode_encoding(flags);
486        let bytes = cursor.read_exact(len as usize)?;
487        Ok(RStr::new(bytes, encoding, self.native_encoding.clone()))
488    }
489
490    fn decode_symbol_with_flags(
491        &mut self,
492        cursor: &mut ByteCursor<'_>,
493        _flags: ItemFlags,
494    ) -> Result<Symbol, Error> {
495        let print_name = self.decode_char_item(cursor)?;
496        let text = print_name
497            .as_str()
498            .ok_or(Error::InvalidSymbolName)?
499            .map_err(|_| Error::InvalidSymbolName)?;
500        let symbol = Symbol::new(Arc::<str>::from(text.as_ref()));
501        self.refs.register(RefEntry::Symbol(symbol.clone()));
502        Ok(symbol)
503    }
504
505    fn decode_persisted(&mut self, cursor: &mut ByteCursor<'_>) -> Result<Persisted, Error> {
506        let values = self.decode_string_vec(cursor)?;
507        let persisted = Persisted::new(values);
508        self.refs.register(RefEntry::Persisted(persisted.clone()));
509        Ok(persisted)
510    }
511
512    /// Decodes the "string vec" payload shared by `PERSISTSXP` and
513    /// `PACKAGESXP`/`NAMESPACESXP`: a discarded i32 placeholder, then an i32
514    /// count (with the usual -1 long-vector escape), then that many
515    /// `CHARSXP` items.
516    fn decode_string_vec(&mut self, cursor: &mut ByteCursor<'_>) -> Result<Vec<RStr>, Error> {
517        let _placeholder = cursor.read_be_i32()?;
518        let offset = cursor.position();
519        let len = cursor.read_be_i32()?;
520        let len = if len == -1 {
521            let len = read_long_len(cursor)?;
522            return Err(Error::PersistedLongVectorUnsupported { len, offset });
523        } else if len < 0 {
524            return Err(Error::NegativeLength { len, offset });
525        } else {
526            len as usize
527        };
528
529        if len > self.limits.max_vector_len_value() {
530            return Err(Error::VectorLengthLimitExceeded {
531                limit: self.limits.max_vector_len_value(),
532                length: len,
533                offset,
534            });
535        }
536        self.account_elements(len, offset)?;
537        (0..len)
538            .map(|_| self.decode_char_item(cursor))
539            .collect::<Result<Vec<_>, _>>()
540    }
541
542    fn decode_attributes(
543        &mut self,
544        cursor: &mut ByteCursor<'_>,
545        depth: u32,
546        mode: Mode,
547    ) -> Result<Attributes, Error> {
548        self.check_depth(depth)?;
549        let flags = self.read_flags(cursor)?;
550        if is_nil(flags) {
551            return Ok(Attributes::default());
552        }
553        let attributes = self.decode_attribute_pairlist_with_flags(cursor, flags, depth, mode)?;
554        Ok(Attributes::new(attributes))
555    }
556
557    fn decode_attribute_pairlist_with_flags(
558        &mut self,
559        cursor: &mut ByteCursor<'_>,
560        flags: ItemFlags,
561        depth: u32,
562        mode: Mode,
563    ) -> Result<Vec<Attribute>, Error> {
564        // The cell's flags were consumed by the caller immediately before
565        // this call; keep their offset so tag errors point at the offending
566        // pairlist cell even after nested attributes advance the cursor.
567        let flags_offset = cursor.position().saturating_sub(4);
568        if flags.type_code() != LISTSXP {
569            return Err(Error::UnsupportedSexp {
570                kind: flags.kind(),
571                type_code: flags.type_code(),
572                offset: flags_offset,
573            });
574        }
575
576        self.account_elements(1, cursor.position())?;
577
578        if flags.has_attributes() {
579            let _ = self.decode_attributes(cursor, depth + 1, mode)?;
580        }
581
582        let name = if flags.has_tag() {
583            self.decode_attribute_tag(cursor, depth + 1)?
584        } else {
585            return Err(Error::InvalidAttributeTag {
586                offset: flags_offset,
587            });
588        };
589
590        let value = self.decode_object(cursor, depth + 1, mode)?;
591        let cdr_flags = self.read_flags(cursor)?;
592        let mut attributes = vec![Attribute::new(name, value)];
593
594        if !is_nil(cdr_flags) {
595            attributes.extend(self.decode_attribute_pairlist_with_flags(
596                cursor,
597                cdr_flags,
598                depth + 1,
599                mode,
600            )?);
601        }
602
603        Ok(attributes)
604    }
605
606    fn decode_attribute_tag(
607        &mut self,
608        cursor: &mut ByteCursor<'_>,
609        depth: u32,
610    ) -> Result<Symbol, Error> {
611        self.check_depth(depth)?;
612        let flags = self.read_flags(cursor)?;
613        match flags.type_code() {
614            SYMSXP => self.decode_symbol_with_flags(cursor, flags),
615            REFSXP => {
616                let inline_index = flags.ref_index_inline();
617                let index = if inline_index == 0 {
618                    cursor.read_be_i32()? as u32
619                } else {
620                    inline_index
621                };
622                match self
623                    .refs
624                    .resolve(index, cursor.position().saturating_sub(4))?
625                {
626                    RefEntry::Symbol(symbol) => Ok(symbol.clone()),
627                    RefEntry::Persisted(_) | RefEntry::Env(_) => Err(Error::InvalidAttributeTag {
628                        offset: cursor.position().saturating_sub(4),
629                    }),
630                }
631            }
632            _ => Err(Error::InvalidAttributeTag {
633                offset: cursor.position().saturating_sub(4),
634            }),
635        }
636    }
637
638    fn read_vector_len(&mut self, cursor: &mut ByteCursor<'_>) -> Result<usize, Error> {
639        let offset = cursor.position();
640        let len = cursor.read_be_i32()?;
641        if len == -1 {
642            let len = read_long_len(cursor)?;
643            return Err(Error::LongVectorUnsupported { len, offset });
644        }
645        if len < 0 {
646            return Err(Error::NegativeLength { len, offset });
647        }
648        let len = len as usize;
649        if len > self.limits.max_vector_len_value() {
650            return Err(Error::VectorLengthLimitExceeded {
651                limit: self.limits.max_vector_len_value(),
652                length: len,
653                offset,
654            });
655        }
656        self.account_elements(len, offset)?;
657        Ok(len)
658    }
659
660    fn check_depth(&self, depth: u32) -> Result<(), Error> {
661        if depth > self.limits.max_depth_value() {
662            Err(Error::DepthLimitExceeded {
663                limit: self.limits.max_depth_value(),
664            })
665        } else {
666            Ok(())
667        }
668    }
669
670    fn account_elements(&mut self, count: usize, offset: usize) -> Result<(), Error> {
671        let total = self.total_elements.saturating_add(count);
672        if total > self.limits.max_total_elements_value() {
673            return Err(Error::TotalElementsLimitExceeded {
674                limit: self.limits.max_total_elements_value(),
675                total,
676                offset,
677            });
678        }
679        self.total_elements = total;
680        Ok(())
681    }
682}
683
684/// Decodes a `CHARSXP` encoding from the `levels` mask bits per R's
685/// `InCharSXP`: UTF-8 (bit 3) takes priority, then Latin-1 (bit 2), then
686/// bytes (bit 1), else native. The ASCII marker (bit 6) is a non-exclusive
687/// hint, not a distinct encoding, so it naturally falls through to Native.
688fn decode_encoding(flags: ItemFlags) -> REncoding {
689    let levels = flags.levels();
690    if levels & (1 << 3) != 0 {
691        REncoding::Utf8
692    } else if levels & (1 << 2) != 0 {
693        REncoding::Latin1
694    } else if levels & (1 << 1) != 0 {
695        REncoding::Bytes
696    } else {
697        REncoding::Native
698    }
699}
700
701fn is_nil(flags: ItemFlags) -> bool {
702    matches!(flags.type_code(), NILSXP | NILVALUE_SXP)
703}
704
705fn is_dotted_pair(flags: ItemFlags) -> bool {
706    matches!(
707        flags.type_code(),
708        LISTSXP | LANGSXP | CLOSXP | PROMSXP | DOTSXP
709    )
710}
711
712fn env_object(handle: EnvHandle) -> RObject {
713    RObject::from_parts(RValue::Environment(handle), Attributes::default())
714}
715
716fn read_long_len(cursor: &mut ByteCursor<'_>) -> Result<u64, Error> {
717    let upper = cursor.read_be_i32()? as u32 as u64;
718    let lower = cursor.read_be_i32()? as u32 as u64;
719    Ok((upper << 32) | lower)
720}
721
722#[cfg(test)]
723mod tests {
724    use super::*;
725    use std::{fs, io::Read, path::PathBuf};
726
727    use flate2::read::GzDecoder;
728
729    fn item(bytes: &[u8]) -> Result<RObject, Error> {
730        item_with_limits(bytes, Limits::default())
731    }
732
733    fn item_with_limits(bytes: &[u8], limits: Limits) -> Result<RObject, Error> {
734        let mut cursor = ByteCursor::new(bytes);
735        Decoder::new(limits, None).decode_root(&mut cursor)
736    }
737
738    fn fixture_dir() -> PathBuf {
739        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/data")
740    }
741
742    fn fixture(name: &str) -> RObject {
743        let bytes = fs::read(fixture_dir().join(name)).expect("fixture bytes");
744        let mut decoder = GzDecoder::new(bytes.as_slice());
745        let mut decompressed = Vec::new();
746        decoder
747            .read_to_end(&mut decompressed)
748            .expect("fixture gzip stream");
749        parse(&decompressed).expect(name)
750    }
751
752    fn rstr(value: &RStr) -> String {
753        value.as_str().unwrap().unwrap().into_owned()
754    }
755
756    fn strings(value: &RObject) -> Vec<String> {
757        let RValue::Character(values) = value.value() else {
758            panic!("expected character vector, got {value:?}");
759        };
760        values.iter().map(rstr).collect()
761    }
762
763    fn list(value: &RObject) -> &[RObject] {
764        let RValue::List(values) = value.value() else {
765            panic!("expected list, got {value:?}");
766        };
767        values
768    }
769
770    fn persisted(value: &RObject) -> &Persisted {
771        let RValue::Persisted(value) = value.value() else {
772            panic!("expected persisted value, got {value:?}");
773        };
774        value
775    }
776
777    fn env_handle(value: &RObject) -> EnvHandle {
778        let RValue::Environment(handle) = value.value() else {
779            panic!("expected environment, got {value:?}");
780        };
781        *handle
782    }
783
784    fn symbol_name(value: &RObject) -> &str {
785        let RValue::Symbol(symbol) = value.value() else {
786            panic!("expected symbol, got {value:?}");
787        };
788        symbol.as_str()
789    }
790
791    #[test]
792    fn decodes_flags_word() {
793        let flags = ItemFlags::from_raw(0x0004_0713);
794        assert_eq!(flags.type_code(), VECSXP);
795        assert!(flags.is_object());
796        assert!(flags.has_attributes());
797        assert!(flags.has_tag());
798        assert_eq!(flags.levels(), 0x40);
799
800        let ref_flags = ItemFlags::from_raw(0x0000_05ff);
801        assert_eq!(ref_flags.type_code(), REFSXP);
802        assert_eq!(ref_flags.ref_index_inline(), 5);
803    }
804
805    #[test]
806    fn decodes_na_string_integer_and_logical() {
807        let charsxp_na = [0, 0, 0, CHARSXP, 0xff, 0xff, 0xff, 0xff];
808        let value = item(&charsxp_na).unwrap();
809        assert_eq!(value.value(), &RValue::Character(vec![RStr::Na]));
810
811        let int_vec = [0, 0, 0, INTSXP, 0, 0, 0, 1, 0x80, 0, 0, 0];
812        let value = item(&int_vec).unwrap();
813        assert_eq!(value.value(), &RValue::Integer(vec![None]));
814
815        let logical_vec = [0, 0, 0, LGLSXP, 0, 0, 0, 2, 0x80, 0, 0, 0, 0, 0, 0, 1];
816        let value = item(&logical_vec).unwrap();
817        assert_eq!(value.value(), &RValue::Logical(vec![None, Some(true)]));
818    }
819
820    #[test]
821    fn charsxp_encoding_levels_bits() {
822        // UTF-8 levels bit (1 << 3) takes priority.
823        let flags: u32 = 9 | (8 << 12);
824        let mut bytes = flags.to_be_bytes().to_vec();
825        bytes.extend_from_slice(&1i32.to_be_bytes());
826        bytes.push(b'a');
827        let value = item(&bytes).unwrap();
828        let RValue::Character(strs) = value.value() else {
829            panic!("expected character vector, got {value:?}");
830        };
831        assert_eq!(strs[0].encoding(), Some(REncoding::Utf8));
832
833        // The ASCII marker bit (1 << 6) is a non-exclusive hint, not a
834        // distinct encoding, so it falls through to Native.
835        let flags: u32 = 9 | (64 << 12);
836        let mut bytes = flags.to_be_bytes().to_vec();
837        bytes.extend_from_slice(&1i32.to_be_bytes());
838        bytes.push(b'a');
839        let value = item(&bytes).unwrap();
840        let RValue::Character(strs) = value.value() else {
841            panic!("expected character vector, got {value:?}");
842        };
843        assert_eq!(strs[0].encoding(), Some(REncoding::Native));
844        assert_eq!(strs[0].as_str().unwrap().unwrap().as_ref(), "a");
845    }
846
847    #[test]
848    fn untagged_attribute_cell_with_nested_attributes_reports_cell_offset() {
849        let mut bytes = Vec::new();
850        // Root: logical vector carrying an attribute pairlist.
851        bytes.extend_from_slice(&(u32::from(LGLSXP) | ATTRIBUTES_BIT).to_be_bytes());
852        bytes.extend_from_slice(&1i32.to_be_bytes());
853        bytes.extend_from_slice(&1i32.to_be_bytes());
854        let cell_flags_offset = bytes.len();
855        // Attribute cell with nested attributes but no tag: decoding the
856        // nested attributes advances the cursor well past the cell's flags.
857        bytes.extend_from_slice(&(u32::from(LISTSXP) | ATTRIBUTES_BIT).to_be_bytes());
858        bytes.extend_from_slice(&(u32::from(LISTSXP) | TAG_BIT).to_be_bytes());
859        bytes.extend_from_slice(&u32::from(SYMSXP).to_be_bytes());
860        bytes.extend_from_slice(&(u32::from(CHARSXP) | (8 << 12)).to_be_bytes());
861        bytes.extend_from_slice(&1i32.to_be_bytes());
862        bytes.push(b'x');
863        bytes.extend_from_slice(&u32::from(NILVALUE_SXP).to_be_bytes());
864        bytes.extend_from_slice(&u32::from(NILVALUE_SXP).to_be_bytes());
865
866        let err = item(&bytes).unwrap_err();
867        assert!(
868            matches!(err, Error::InvalidAttributeTag { offset } if offset == cell_flags_offset),
869            "expected InvalidAttributeTag at {cell_flags_offset}, got {err:?}"
870        );
871    }
872
873    #[test]
874    fn singleton_env_byte_level_decoding() {
875        for (byte, expected) in [
876            (253u8, EnvHandle::Global),
877            (241u8, EnvHandle::Base),
878            (242u8, EnvHandle::Empty),
879            (250u8, EnvHandle::Base),
880        ] {
881            let bytes = [0, 0, 0, byte];
882            let mut cursor = ByteCursor::new(&bytes);
883            let value = Decoder::new(Limits::default(), None)
884                .decode_root(&mut cursor)
885                .unwrap();
886            assert_eq!(value.value(), &RValue::Environment(expected));
887            assert_eq!(cursor.remaining(), 0);
888        }
889    }
890
891    #[test]
892    fn environment_frame_pairlist_cells_count_toward_total_elements_limit() {
893        let mut bytes = Vec::new();
894        bytes.extend_from_slice(&u32::from(ENVSXP).to_be_bytes());
895        bytes.extend_from_slice(&0i32.to_be_bytes());
896        bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // enclos
897
898        bytes.extend_from_slice(&u32::from(LISTSXP).to_be_bytes());
899        for index in 0..3 {
900            bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // CAR
901            let cdr = if index == 2 { 0 } else { u32::from(LISTSXP) };
902            bytes.extend_from_slice(&cdr.to_be_bytes());
903        }
904        bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // hashtab
905        bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // attrib
906
907        let error = item_with_limits(&bytes, Limits::default().max_total_elements(2))
908            .expect_err("frame pairlist should exceed the element limit");
909        assert!(matches!(
910            error,
911            Error::TotalElementsLimitExceeded { limit: 2, .. }
912        ));
913    }
914
915    #[test]
916    fn compliant_environment_frame_pairlist_decodes_to_other() {
917        let mut bytes = Vec::new();
918        bytes.extend_from_slice(&u32::from(ENVSXP).to_be_bytes());
919        bytes.extend_from_slice(&0i32.to_be_bytes());
920        bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // enclos
921        bytes.extend_from_slice(&u32::from(LISTSXP).to_be_bytes());
922        bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // CAR
923        bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // CDR
924        bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // hashtab
925        bytes.extend_from_slice(&u32::from(0u8).to_be_bytes()); // attrib
926
927        let value = item_with_limits(&bytes, Limits::default().max_total_elements(1))
928            .expect("compliant environment should decode");
929        assert_eq!(env_handle(&value), EnvHandle::Other);
930    }
931
932    #[test]
933    fn refsxp_out_of_range_index_is_reported() {
934        let err = item(&[0, 0, 1, REFSXP]).unwrap_err();
935        assert_eq!(
936            err,
937            Error::RefIndexOutOfRange {
938                index: 1,
939                len: 0,
940                offset: 0
941            }
942        );
943    }
944
945    #[test]
946    fn persistsxp_long_vector_escape_is_reported() {
947        let err = item(&[
948            0, 0, 0, PERSISTSXP, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 1, 0, 0, 0, 2,
949        ])
950        .unwrap_err();
951        assert_eq!(
952            err,
953            Error::PersistedLongVectorUnsupported {
954                len: 0x1_0000_0002,
955                offset: 8
956            }
957        );
958    }
959
960    #[test]
961    fn unsupported_type_is_reported() {
962        let err = item(&[0, 0, 0, EXTPTRSXP]).unwrap_err();
963        assert_eq!(
964            err,
965            Error::UnsupportedSexp {
966                kind: SexpKind::ExtPtr,
967                type_code: EXTPTRSXP,
968                offset: 0
969            }
970        );
971    }
972
973    #[test]
974    fn strict_mode_rejects_dotted_pair_at_top_level() {
975        let err = item(&[0, 0, 0, CLOSXP]).unwrap_err();
976        assert_eq!(
977            err,
978            Error::UnsupportedSexp {
979                kind: SexpKind::Closure,
980                type_code: CLOSXP,
981                offset: 0
982            }
983        );
984    }
985
986    #[test]
987    fn decodes_aliases_vector_fixtures() {
988        for version in [2, 3] {
989            let root = fixture(&format!("aliases_vector_v{version}.rds"));
990            assert_eq!(
991                strings(&root),
992                vec![
993                    "minimal",
994                    "multialias",
995                    "multialias",
996                    "multialias",
997                    "multialias"
998                ]
999            );
1000            assert_eq!(
1001                root.names().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1002                vec![
1003                    "minimal",
1004                    "multialias",
1005                    "multialias-method",
1006                    "multialias.default",
1007                    "print.multialias"
1008                ]
1009            );
1010        }
1011    }
1012
1013    #[test]
1014    fn decodes_shared_symbols_fixtures() {
1015        for version in [2, 3] {
1016            let root = fixture(&format!("shared_symbols_v{version}.rds"));
1017            assert_eq!(
1018                root.names().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1019                vec!["a", "b", "d"]
1020            );
1021            let items = list(&root);
1022            assert_eq!(items.len(), 3);
1023
1024            assert_eq!(
1025                items[0]
1026                    .class()
1027                    .unwrap()
1028                    .iter()
1029                    .map(rstr)
1030                    .collect::<Vec<_>>(),
1031                vec!["widget"]
1032            );
1033            assert_eq!(
1034                strings(items[0].attributes().get("note").unwrap()),
1035                vec!["first"]
1036            );
1037
1038            let b_items = list(&items[1]);
1039            assert_eq!(
1040                items[1]
1041                    .class()
1042                    .unwrap()
1043                    .iter()
1044                    .map(rstr)
1045                    .collect::<Vec<_>>(),
1046                vec!["widget"]
1047            );
1048            assert_eq!(
1049                strings(items[1].attributes().get("note").unwrap()),
1050                vec!["third"]
1051            );
1052            assert_eq!(
1053                items[1]
1054                    .names()
1055                    .unwrap()
1056                    .iter()
1057                    .map(rstr)
1058                    .collect::<Vec<_>>(),
1059                vec!["c"]
1060            );
1061            assert_eq!(
1062                b_items[0]
1063                    .class()
1064                    .unwrap()
1065                    .iter()
1066                    .map(rstr)
1067                    .collect::<Vec<_>>(),
1068                vec!["widget"]
1069            );
1070            assert_eq!(
1071                strings(b_items[0].attributes().get("note").unwrap()),
1072                vec!["second"]
1073            );
1074
1075            assert_eq!(strings(&items[2]), vec!["x"]);
1076            assert_eq!(
1077                items[2]
1078                    .class()
1079                    .unwrap()
1080                    .iter()
1081                    .map(rstr)
1082                    .collect::<Vec<_>>(),
1083                vec!["widget"]
1084            );
1085            assert_eq!(
1086                strings(items[2].attributes().get("note").unwrap()),
1087                vec!["fourth"]
1088            );
1089        }
1090    }
1091
1092    #[test]
1093    fn decodes_persistsxp_basic_fixtures() {
1094        for version in [2, 3] {
1095            let root = fixture(&format!("persistsxp_basic_v{version}.rds"));
1096            assert_eq!(
1097                root.names().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1098                vec!["env", "tail"]
1099            );
1100            let items = list(&root);
1101            assert_eq!(
1102                persisted(&items[0])
1103                    .as_slice()
1104                    .iter()
1105                    .map(rstr)
1106                    .collect::<Vec<_>>(),
1107                vec!["srcref-env"]
1108            );
1109            assert_eq!(strings(&items[1]), vec!["tail-marker"]);
1110        }
1111    }
1112
1113    #[test]
1114    fn decodes_persistsxp_twice_fixtures() {
1115        for version in [2, 3] {
1116            let root = fixture(&format!("persistsxp_twice_v{version}.rds"));
1117            assert_eq!(
1118                root.names().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1119                vec!["first", "second", "after"]
1120            );
1121            let items = list(&root);
1122            let first = persisted(&items[0]);
1123            let second = persisted(&items[1]);
1124            assert_eq!(
1125                first.as_slice().iter().map(rstr).collect::<Vec<_>>(),
1126                vec!["srcref-env"]
1127            );
1128            assert_eq!(
1129                second.as_slice().iter().map(rstr).collect::<Vec<_>>(),
1130                vec!["srcref-env"]
1131            );
1132            assert!(!first.ptr_eq(second));
1133            assert_eq!(strings(&items[2]), vec!["tail-marker"]);
1134        }
1135    }
1136
1137    #[test]
1138    fn decodes_persistsxp_multi_fixtures() {
1139        for version in [2, 3] {
1140            let root = fixture(&format!("persistsxp_multi_v{version}.rds"));
1141            let items = list(&root);
1142            assert_eq!(
1143                persisted(&items[0])
1144                    .as_slice()
1145                    .iter()
1146                    .map(rstr)
1147                    .collect::<Vec<_>>(),
1148                vec!["a", "b", "c"]
1149            );
1150            assert_eq!(strings(&items[1]), vec!["tail-marker"]);
1151        }
1152    }
1153
1154    #[test]
1155    fn decodes_singleton_envs_fixtures() {
1156        for version in [2, 3] {
1157            let root = fixture(&format!("singleton_envs_v{version}.rds"));
1158            assert_eq!(
1159                root.names().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1160                vec!["global", "base", "empty"]
1161            );
1162            let items = list(&root);
1163            assert_eq!(env_handle(&items[0]), EnvHandle::Global);
1164            assert_eq!(env_handle(&items[1]), EnvHandle::Base);
1165            assert_eq!(env_handle(&items[2]), EnvHandle::Empty);
1166        }
1167    }
1168
1169    #[test]
1170    fn decodes_plain_env_fixtures() {
1171        for version in [2, 3] {
1172            let root = fixture(&format!("plain_env_v{version}.rds"));
1173            assert_eq!(env_handle(&root), EnvHandle::Other);
1174            assert!(root.attributes().is_empty());
1175        }
1176    }
1177
1178    #[test]
1179    fn decodes_env_with_closure_fixtures() {
1180        for version in [2, 3] {
1181            let root = fixture(&format!("env_with_closure_v{version}.rds"));
1182            assert_eq!(
1183                root.names().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1184                vec!["env", "tail"]
1185            );
1186            let items = list(&root);
1187            assert_eq!(items[0].value(), &RValue::Environment(EnvHandle::Other));
1188            assert_eq!(strings(&items[1]), vec!["tail-marker"]);
1189        }
1190    }
1191
1192    #[test]
1193    fn decodes_shared_env_refs_fixtures() {
1194        for version in [2, 3] {
1195            let root = fixture(&format!("shared_env_refs_v{version}.rds"));
1196            assert_eq!(
1197                root.names().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1198                vec!["vec", "sym_a", "env_first", "sym_b", "env_second"]
1199            );
1200            let items = list(&root);
1201            assert_eq!(
1202                items[0].value(),
1203                &RValue::Integer(vec![Some(4), Some(2), Some(7)])
1204            );
1205            assert_eq!(symbol_name(&items[1]), "dup_sym");
1206            assert_eq!(env_handle(&items[2]), EnvHandle::Other);
1207            assert_eq!(symbol_name(&items[3]), "dup_sym");
1208            assert_eq!(env_handle(&items[4]), EnvHandle::Other);
1209        }
1210    }
1211
1212    /// ALTREP is deliberately out of scope for this decoder: real help DBs
1213    /// never contain it in value trees, so it fails with
1214    /// `Error::UnsupportedSexp` rather than being modeled.
1215    #[test]
1216    fn altrep_is_rejected() {
1217        let bytes = fs::read(fixture_dir().join("altrep_intseq_v3.rds")).expect("fixture bytes");
1218        let mut decoder = GzDecoder::new(bytes.as_slice());
1219        let mut decompressed = Vec::new();
1220        decoder
1221            .read_to_end(&mut decompressed)
1222            .expect("fixture gzip stream");
1223        let err = parse(&decompressed).unwrap_err();
1224        assert_eq!(
1225            err,
1226            Error::UnsupportedSexp {
1227                kind: SexpKind::Other(238),
1228                type_code: 238,
1229                offset: 23
1230            }
1231        );
1232    }
1233
1234    #[test]
1235    fn decodes_namespace_refs_fixtures() {
1236        for version in [2, 3] {
1237            let root = fixture(&format!("namespace_refs_v{version}.rds"));
1238            assert_eq!(
1239                root.names().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1240                vec!["ns_first", "ns_second", "tail"]
1241            );
1242            let items = list(&root);
1243            assert_eq!(items[0].value(), &RValue::Environment(EnvHandle::Other));
1244            assert_eq!(items[1].value(), &RValue::Environment(EnvHandle::Other));
1245            assert_eq!(strings(&items[2]), vec!["tail-marker"]);
1246        }
1247    }
1248
1249    #[test]
1250    fn decodes_rd_fixtures_as_rd_class_lists() {
1251        for name in ["rd_minimal", "rd_aliases", "rd_arguments", "rd_seealso"] {
1252            for version in [2, 3] {
1253                let root = fixture(&format!("{name}_v{version}.rds"));
1254                assert!(matches!(root.value(), &RValue::List(_)));
1255                assert_eq!(
1256                    root.class().unwrap().iter().map(rstr).collect::<Vec<_>>(),
1257                    vec!["Rd"]
1258                );
1259                if name == "rd_seealso" {
1260                    assert!(root.attributes().get("srcref").is_some());
1261                }
1262
1263                // parse_Rd invariant: every list node carries an "Rd_tag"
1264                // attribute (text leaves are Character vectors with Rd_tag
1265                // too). Checked robustly rather than exhaustively: at least
1266                // the first element has it, and at least one element among
1267                // the root's children has it.
1268                let items = list(&root);
1269                assert!(!items.is_empty(), "{name}_v{version}: empty root list");
1270                let mut tagged_count = 0usize;
1271                for (index, item) in items.iter().enumerate() {
1272                    if matches!(item.value(), &RValue::List(_)) {
1273                        let rd_tag = item.attributes().get("Rd_tag");
1274                        if index == 0 {
1275                            assert!(
1276                                rd_tag.is_some(),
1277                                "{name}_v{version}: first element missing Rd_tag"
1278                            );
1279                        }
1280                        if let Some(rd_tag) = rd_tag {
1281                            assert!(
1282                                matches!(rd_tag.value(), &RValue::Character(_)),
1283                                "{name}_v{version}: Rd_tag value is not a character vector"
1284                            );
1285                            tagged_count += 1;
1286                        }
1287                    }
1288                }
1289                assert!(
1290                    tagged_count >= 1,
1291                    "{name}_v{version}: no list element carries Rd_tag"
1292                );
1293            }
1294        }
1295    }
1296}