Skip to main content

pdfboss_core/
parser.rs

1//! Object parser built on the [`Lexer`], including indirect objects and
2//! streams (ISO 32000 §7.3.8/§7.3.10).
3
4use crate::error::{Error, Result};
5use crate::lexer::{Lexer, Token};
6use crate::object::{Dict, ObjRef, Object, Stream};
7
8/// Maximum container (array/dictionary) nesting depth. Deeper input is
9/// rejected with a syntax error: the parser recurses per nesting level, so
10/// without a bound a tiny crafted file (e.g. 100k `[`s) overflows the
11/// stack and aborts the whole process. Real documents nest a handful of
12/// levels; 128 is far beyond anything legitimate.
13const MAX_NESTING_DEPTH: usize = 128;
14
15/// Resolves indirect references while parsing (e.g. an indirect `/Length`).
16pub trait Resolve {
17    /// Returns the referenced object, or `None` if it cannot be resolved.
18    fn resolve_ref(&self, r: ObjRef) -> Option<Object>;
19}
20
21/// A [`Resolve`] implementation that never resolves anything.
22pub struct NoResolve;
23
24impl Resolve for NoResolve {
25    fn resolve_ref(&self, _r: ObjRef) -> Option<Object> {
26        None
27    }
28}
29
30/// Recursive-descent parser over a byte slice.
31pub struct Parser<'a> {
32    lexer: Lexer<'a>,
33}
34
35impl<'a> Parser<'a> {
36    /// Creates a parser at the start of `data`.
37    pub fn new(data: &'a [u8]) -> Self {
38        Parser {
39            lexer: Lexer::new(data),
40        }
41    }
42
43    /// Creates a parser positioned at byte offset `pos`.
44    pub fn at(data: &'a [u8], pos: usize) -> Self {
45        Parser {
46            lexer: Lexer::at(data, pos),
47        }
48    }
49
50    /// Current byte offset.
51    pub fn pos(&self) -> usize {
52        self.lexer.pos()
53    }
54
55    /// Moves the cursor to byte offset `pos`.
56    pub fn seek(&mut self, pos: usize) {
57        self.lexer.seek(pos);
58    }
59
60    /// Parses one object at the current position. `int int R` becomes
61    /// [`Object::Ref`] via lookahead with backtracking; dictionaries
62    /// followed by `stream` become [`Object::Stream`] (using `resolver`
63    /// for an indirect `/Length`, with `endstream` scan recovery).
64    pub fn parse_object(&mut self, resolver: &dyn Resolve) -> Result<Object> {
65        let token = self.lexer.next_token()?;
66        self.parse_from_token(token, resolver, 0)
67    }
68
69    /// Builds a [`Error::Syntax`] at the current position.
70    fn syntax(&self, msg: impl Into<String>) -> Error {
71        Error::Syntax {
72            offset: self.lexer.pos(),
73            msg: msg.into(),
74        }
75    }
76
77    /// Parses the object that `token` begins. `depth` counts enclosing
78    /// containers (bounded by `MAX_NESTING_DEPTH`).
79    ///
80    /// Covers ISO 32000-1 §7.3.2, §7.3.9 and §7.9.2.4.
81    fn parse_from_token(
82        &mut self,
83        token: Token,
84        resolver: &dyn Resolve,
85        depth: usize,
86    ) -> Result<Object> {
87        match token {
88            Token::Int(i) => Ok(self.try_reference(i)),
89            Token::Real(r) => Ok(Object::Real(r)),
90            Token::Name(n) => Ok(Object::Name(n)),
91            Token::LitString(s) | Token::HexString(s) => Ok(Object::String(s)),
92            Token::ArrayOpen => self.parse_array(resolver, depth),
93            Token::DictOpen => self.parse_dict_or_stream(resolver, depth),
94            Token::Keyword(k) => match k.as_slice() {
95                b"true" => Ok(Object::Bool(true)),
96                b"false" => Ok(Object::Bool(false)),
97                b"null" => Ok(Object::Null),
98                _ => Err(self.syntax(format!(
99                    "unexpected keyword `{}`",
100                    String::from_utf8_lossy(&k)
101                ))),
102            },
103            Token::ArrayClose => Err(self.syntax("unexpected `]`")),
104            Token::DictClose => Err(self.syntax("unexpected `>>`")),
105            Token::Eof => Err(self.syntax("unexpected end of input")),
106        }
107    }
108
109    /// After an integer has been read: if `int R` follows, the three tokens
110    /// form an indirect reference; otherwise the lexer is rewound so the
111    /// integer stands alone.
112    ///
113    /// Covers ISO 32000-1 §7.3.10.
114    fn try_reference(&mut self, num: i64) -> Object {
115        let save = self.lexer.pos();
116        if (0..=i64::from(u32::MAX)).contains(&num) {
117            if let Ok(Token::Int(gen)) = self.lexer.next_token() {
118                if (0..=i64::from(u16::MAX)).contains(&gen)
119                    && matches!(self.lexer.next_token(),
120                                Ok(Token::Keyword(ref k)) if k.as_slice() == b"R")
121                {
122                    return Object::Ref(ObjRef {
123                        num: num as u32,
124                        gen: gen as u16,
125                    });
126                }
127            }
128        }
129        self.lexer.seek(save);
130        Object::Int(num)
131    }
132
133    /// Parses array elements up to `]` (leniently also up to end of input).
134    ///
135    /// Covers ISO 32000-1 §7.3.6.
136    fn parse_array(&mut self, resolver: &dyn Resolve, depth: usize) -> Result<Object> {
137        if depth >= MAX_NESTING_DEPTH {
138            return Err(self.syntax("container nesting too deep"));
139        }
140        let mut items = Vec::new();
141        loop {
142            let token = self.lexer.next_token()?;
143            match token {
144                Token::ArrayClose | Token::Eof => break,
145                other => items.push(self.parse_from_token(other, resolver, depth + 1)?),
146            }
147        }
148        Ok(Object::Array(items))
149    }
150
151    /// Parses dictionary entries up to `>>`; if the `stream` keyword follows,
152    /// the dictionary becomes a stream's dictionary and the stream data is
153    /// read as well.
154    ///
155    /// Covers ISO 32000-1 §7.3.7 and §7.3.8.
156    fn parse_dict_or_stream(&mut self, resolver: &dyn Resolve, depth: usize) -> Result<Object> {
157        if depth >= MAX_NESTING_DEPTH {
158            return Err(self.syntax("container nesting too deep"));
159        }
160        let mut dict = Dict::new();
161        loop {
162            match self.lexer.next_token()? {
163                Token::DictClose | Token::Eof => break,
164                Token::Name(key) => {
165                    let token = self.lexer.next_token()?;
166                    if token == Token::DictClose {
167                        // Lenient: a key with no value maps to null.
168                        dict.insert(key, Object::Null);
169                        break;
170                    }
171                    let value = self.parse_from_token(token, resolver, depth + 1)?;
172                    dict.insert(key, value);
173                }
174                // Lenient: a non-name key is consumed as an object and dropped.
175                other => {
176                    self.parse_from_token(other, resolver, depth + 1)?;
177                }
178            }
179        }
180        if matches!(self.lexer.peek_token(),
181                    Ok(Token::Keyword(ref k)) if k.as_slice() == b"stream")
182        {
183            self.lexer.next_token()?;
184            return self.parse_stream_body(dict, resolver);
185        }
186        Ok(Object::Dict(dict))
187    }
188
189    /// Reads stream data after the `stream` keyword (ISO 32000 §7.3.8):
190    /// skip the EOL after the keyword, take `/Length` bytes (resolving an
191    /// indirect length via `resolver`), and verify `endstream` follows. When
192    /// `/Length` is missing or wrong, recover by scanning for the nearest
193    /// `endstream` and trimming one trailing EOL from the data.
194    ///
195    /// Covers ISO 32000-1 §7.3.8, §7.3.8.1 and §7.3.8.2.
196    fn parse_stream_body(&mut self, dict: Dict, resolver: &dyn Resolve) -> Result<Object> {
197        let data = self.lexer.data();
198        let mut start = self.lexer.pos();
199        // The keyword should be followed by CRLF or LF; tolerate a lone CR.
200        if data.get(start) == Some(&b'\r') {
201            start += 1;
202        }
203        if data.get(start) == Some(&b'\n') {
204            start += 1;
205        }
206        let declared = match dict.get("Length") {
207            Some(Object::Int(n)) => Some(*n),
208            Some(Object::Ref(r)) => resolver.resolve_ref(*r).and_then(|o| o.as_int()),
209            _ => None,
210        };
211        if let Some(len) = declared.filter(|&l| l >= 0) {
212            if let Some(end) = start.checked_add(len as usize).filter(|&e| e <= data.len()) {
213                let mut probe = Lexer::at(data, end);
214                if matches!(probe.next_token(),
215                            Ok(Token::Keyword(ref k)) if k.as_slice() == b"endstream")
216                {
217                    self.lexer.seek(probe.pos());
218                    return Ok(Object::Stream(Stream {
219                        dict,
220                        data: data[start..end].to_vec(),
221                    }));
222                }
223            }
224        }
225        match memchr::memmem::find(&data[start..], b"endstream") {
226            Some(idx) => {
227                let mut end = start + idx;
228                if end > start && data[end - 1] == b'\n' {
229                    end -= 1;
230                }
231                if end > start && data[end - 1] == b'\r' {
232                    end -= 1;
233                }
234                self.lexer.seek(start + idx + b"endstream".len());
235                Ok(Object::Stream(Stream {
236                    dict,
237                    data: data[start..end].to_vec(),
238                }))
239            }
240            None => {
241                // Lenient: an unterminated stream takes the rest of the input.
242                self.lexer.seek(data.len());
243                Ok(Object::Stream(Stream {
244                    dict,
245                    data: data[start..].to_vec(),
246                }))
247            }
248        }
249    }
250
251    /// Expects `N G obj ... endobj` at the current position and returns the
252    /// reference plus the contained object. Lenient: a missing `endobj` is
253    /// accepted at the next `obj` or end of input.
254    ///
255    /// Covers ISO 32000-1 §7.3.10 and §7.5.3.
256    pub fn parse_indirect(&mut self, resolver: &dyn Resolve) -> Result<(ObjRef, Object)> {
257        let num = match self.lexer.next_token()? {
258            Token::Int(n) if (0..=i64::from(u32::MAX)).contains(&n) => n as u32,
259            _ => return Err(self.syntax("expected object number")),
260        };
261        let gen = match self.lexer.next_token()? {
262            Token::Int(g) if (0..=i64::from(u16::MAX)).contains(&g) => g as u16,
263            _ => return Err(self.syntax("expected generation number")),
264        };
265        match self.lexer.next_token()? {
266            Token::Keyword(ref k) if k.as_slice() == b"obj" => {}
267            _ => return Err(self.syntax("expected `obj`")),
268        }
269        // Lenient: an empty body (`N G obj endobj`) yields null.
270        let object = match self.lexer.peek_token() {
271            Ok(Token::Keyword(ref k)) if k.as_slice() == b"endobj" => Object::Null,
272            _ => self.parse_object(resolver)?,
273        };
274        // Lenient: consume `endobj` when present; a missing `endobj` is
275        // accepted as-is (at the next `N G obj` header or end of input).
276        let save = self.lexer.pos();
277        match self.lexer.next_token() {
278            Ok(Token::Keyword(ref k)) if k.as_slice() == b"endobj" => {}
279            _ => self.lexer.seek(save),
280        }
281        Ok((ObjRef { num, gen }, object))
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288    use crate::object::Name;
289
290    fn parse(data: &[u8]) -> Object {
291        Parser::new(data).parse_object(&NoResolve).unwrap()
292    }
293
294    // Covers ISO 32000-1 §7.3.2 and §7.3.9.
295    #[test]
296    fn atom_null_and_bools() {
297        assert_eq!(parse(b"null"), Object::Null);
298        assert_eq!(parse(b"true"), Object::Bool(true));
299        assert_eq!(parse(b"false"), Object::Bool(false));
300    }
301
302    // Covers ISO 32000-1 §7.3.3.
303    #[test]
304    fn atom_numbers() {
305        assert_eq!(parse(b"42"), Object::Int(42));
306        assert_eq!(parse(b"-17"), Object::Int(-17));
307        assert_eq!(parse(b"+5"), Object::Int(5));
308        assert_eq!(parse(b"3.5"), Object::Real(3.5));
309        assert_eq!(parse(b"-.25"), Object::Real(-0.25));
310    }
311
312    // Covers ISO 32000-1 §7.3.4 and §7.9.2.4.
313    #[test]
314    fn atom_strings() {
315        assert_eq!(parse(b"(hello)"), Object::String(b"hello".to_vec()));
316        assert_eq!(parse(b"<48690A>"), Object::String(b"Hi\n".to_vec()));
317    }
318
319    #[test]
320    fn atom_name() {
321        assert_eq!(parse(b"/Type"), Object::Name(Name("Type".into())));
322    }
323
324    // Covers ISO 32000-1 §7.3.6 and §7.3.7.
325    #[test]
326    fn nested_arrays_and_dicts() {
327        let obj = parse(b"<< /A [1 2 [3]] /B << /C /D >> >>");
328        let dict = obj.as_dict().unwrap();
329        let a = dict.get_array("A").unwrap();
330        assert_eq!(a[0], Object::Int(1));
331        assert_eq!(a[1], Object::Int(2));
332        assert_eq!(a[2], Object::Array(vec![Object::Int(3)]));
333        let b = dict.get_dict("B").unwrap();
334        assert_eq!(b.get_name("C"), Some(&Name("D".into())));
335    }
336
337    // Covers ISO 32000-1 §7.3.10.
338    #[test]
339    fn reference_from_lookahead() {
340        assert_eq!(parse(b"12 0 R"), Object::Ref(ObjRef { num: 12, gen: 0 }));
341        assert_eq!(
342            parse(b"[12 0 R 7 3 R]"),
343            Object::Array(vec![
344                Object::Ref(ObjRef { num: 12, gen: 0 }),
345                Object::Ref(ObjRef { num: 7, gen: 3 }),
346            ])
347        );
348    }
349
350    // Covers ISO 32000-1 §7.3.10.
351    #[test]
352    fn two_ints_without_r_stay_ints() {
353        let mut p = Parser::new(b"12 0");
354        assert_eq!(p.parse_object(&NoResolve).unwrap(), Object::Int(12));
355        assert_eq!(p.parse_object(&NoResolve).unwrap(), Object::Int(0));
356        assert_eq!(
357            parse(b"[12 0]"),
358            Object::Array(vec![Object::Int(12), Object::Int(0)])
359        );
360        // `RG` is a different keyword, not `R`: no reference.
361        let mut p = Parser::new(b"12 0 RG");
362        assert_eq!(p.parse_object(&NoResolve).unwrap(), Object::Int(12));
363        assert_eq!(p.parse_object(&NoResolve).unwrap(), Object::Int(0));
364    }
365
366    // Covers ISO 32000-1 §7.3.10 and §7.5.3.
367    #[test]
368    fn indirect_round_trip() {
369        let mut p = Parser::new(b"1 0 obj << /Type /Test /N 3 >> endobj");
370        let (r, obj) = p.parse_indirect(&NoResolve).unwrap();
371        assert_eq!(r, ObjRef { num: 1, gen: 0 });
372        let dict = obj.as_dict().unwrap();
373        assert_eq!(dict.get_name("Type"), Some(&Name("Test".into())));
374        assert_eq!(dict.get_int("N"), Some(3));
375        assert_eq!(p.lexer.next_token().unwrap(), Token::Eof);
376    }
377
378    // Covers ISO 32000-1 §7.3.8 and §7.3.8.1.
379    #[test]
380    fn stream_with_direct_length() {
381        let mut p = Parser::new(b"<< /Length 5 >>\nstream\nhello\nendstream");
382        let obj = p.parse_object(&NoResolve).unwrap();
383        let s = obj.as_stream().unwrap();
384        assert_eq!(s.data, b"hello");
385        assert_eq!(p.lexer.next_token().unwrap(), Token::Eof);
386    }
387
388    /// Resolves exactly one reference, for indirect `/Length` tests.
389    struct OneResolve(ObjRef, Object);
390
391    impl Resolve for OneResolve {
392        fn resolve_ref(&self, r: ObjRef) -> Option<Object> {
393            (r == self.0).then(|| self.1.clone())
394        }
395    }
396
397    // Covers ISO 32000-1 §7.3.8.2.
398    #[test]
399    fn stream_with_indirect_length() {
400        let resolver = OneResolve(ObjRef { num: 9, gen: 0 }, Object::Int(7));
401        let mut p = Parser::new(b"<< /Length 9 0 R >>stream\r\nhello!!\r\nendstream");
402        let obj = p.parse_object(&resolver).unwrap();
403        assert_eq!(obj.as_stream().unwrap().data, b"hello!!");
404    }
405
406    // Covers ISO 32000-1 §7.3.8.2.
407    #[test]
408    fn stream_with_wrong_length_recovers() {
409        let mut p = Parser::new(b"<< /Length 3 >>stream\nhello world\nendstream endobj");
410        let obj = p.parse_object(&NoResolve).unwrap();
411        assert_eq!(obj.as_stream().unwrap().data, b"hello world");
412        // Recovery leaves the cursor right after `endstream`.
413        assert_eq!(
414            p.lexer.next_token().unwrap(),
415            Token::Keyword(b"endobj".to_vec())
416        );
417    }
418
419    // Covers ISO 32000-1 §7.3.8.2.
420    #[test]
421    fn stream_with_overlong_length_recovers() {
422        let mut p = Parser::new(b"<< /Length 999 >>stream\nxy\r\nendstream");
423        let obj = p.parse_object(&NoResolve).unwrap();
424        assert_eq!(obj.as_stream().unwrap().data, b"xy");
425    }
426
427    // Covers ISO 32000-1 §7.3.8.2.
428    #[test]
429    fn stream_without_length_recovers() {
430        let mut p = Parser::new(b"<< /Type /XObject >>stream\nabc\nendstream");
431        let obj = p.parse_object(&NoResolve).unwrap();
432        let s = obj.as_stream().unwrap();
433        assert_eq!(s.data, b"abc");
434        assert_eq!(s.dict.get_name("Type"), Some(&Name("XObject".into())));
435    }
436
437    // Covers ISO 32000-1 §7.3.8.2.
438    #[test]
439    fn stream_with_unresolvable_length_recovers() {
440        // The resolver knows nothing, so the indirect length falls through
441        // to the `endstream` scan.
442        let mut p = Parser::new(b"<< /Length 8 0 R >>stream\ndata\nendstream");
443        let obj = p.parse_object(&NoResolve).unwrap();
444        assert_eq!(obj.as_stream().unwrap().data, b"data");
445    }
446
447    // Covers ISO 32000-1 §7.3.10.
448    #[test]
449    fn missing_endobj_accepted_at_next_obj() {
450        let mut p = Parser::new(b"1 0 obj 42 2 0 obj (next) endobj");
451        let (r1, o1) = p.parse_indirect(&NoResolve).unwrap();
452        assert_eq!(r1, ObjRef { num: 1, gen: 0 });
453        assert_eq!(o1, Object::Int(42));
454        let (r2, o2) = p.parse_indirect(&NoResolve).unwrap();
455        assert_eq!(r2, ObjRef { num: 2, gen: 0 });
456        assert_eq!(o2, Object::String(b"next".to_vec()));
457    }
458
459    #[test]
460    fn missing_endobj_accepted_at_eof() {
461        let mut p = Parser::new(b"5 0 obj << /A 1 >>");
462        let (r, obj) = p.parse_indirect(&NoResolve).unwrap();
463        assert_eq!(r, ObjRef { num: 5, gen: 0 });
464        assert_eq!(obj.as_dict().unwrap().get_int("A"), Some(1));
465    }
466
467    #[test]
468    fn empty_indirect_body_is_null() {
469        let mut p = Parser::new(b"3 1 obj endobj");
470        let (r, obj) = p.parse_indirect(&NoResolve).unwrap();
471        assert_eq!(r, ObjRef { num: 3, gen: 1 });
472        assert_eq!(obj, Object::Null);
473        assert_eq!(p.lexer.next_token().unwrap(), Token::Eof);
474    }
475
476    // Covers ISO 32000-1 §7.3.8.
477    #[test]
478    fn indirect_stream_object() {
479        let data = b"4 0 obj << /Length 3 >> stream\nxyz\nendstream endobj";
480        let mut p = Parser::new(data);
481        let (r, obj) = p.parse_indirect(&NoResolve).unwrap();
482        assert_eq!(r, ObjRef { num: 4, gen: 0 });
483        assert_eq!(obj.as_stream().unwrap().data, b"xyz");
484        assert_eq!(p.lexer.next_token().unwrap(), Token::Eof);
485    }
486
487    // Covers ISO 32000-1 §7.3.7.
488    #[test]
489    fn dict_value_reference() {
490        let obj = parse(b"<< /Parent 6 0 R /Count 2 >>");
491        let dict = obj.as_dict().unwrap();
492        assert_eq!(dict.get_ref("Parent"), Some(ObjRef { num: 6, gen: 0 }));
493        assert_eq!(dict.get_int("Count"), Some(2));
494    }
495
496    /// Runs `f` on a deliberately small stack so that unbounded recursion
497    /// would abort the test binary instead of silently passing on a large
498    /// main-thread stack.
499    fn on_small_stack<T: Send + 'static>(f: impl FnOnce() -> T + Send + 'static) -> T {
500        std::thread::Builder::new()
501            .stack_size(512 * 1024)
502            .spawn(f)
503            .expect("spawn test thread")
504            .join()
505            .expect("parser must not overflow the stack")
506    }
507
508    // Covers ISO 32000-1 §7.3.6 and Annex C.2.
509    #[test]
510    fn deeply_nested_array_is_rejected_not_stack_overflow() {
511        let mut data = vec![b'['; 200_000];
512        data.extend(std::iter::repeat_n(b']', 200_000));
513        let result = on_small_stack(move || Parser::new(&data).parse_object(&NoResolve));
514        assert!(matches!(result, Err(Error::Syntax { .. })));
515    }
516
517    // Covers ISO 32000-1 §7.3.7.
518    #[test]
519    fn deeply_nested_dict_is_rejected_not_stack_overflow() {
520        let mut data = Vec::new();
521        for _ in 0..100_000 {
522            data.extend_from_slice(b"<</K");
523        }
524        let result = on_small_stack(move || Parser::new(&data).parse_object(&NoResolve));
525        assert!(matches!(result, Err(Error::Syntax { .. })));
526    }
527
528    // Covers ISO 32000-1 §7.3.6 and Annex C.2.
529    #[test]
530    fn nesting_within_the_limit_still_parses() {
531        let mut data = vec![b'['; 100];
532        data.extend_from_slice(b" 7 ");
533        data.extend(std::iter::repeat_n(b']', 100));
534        let mut obj = parse(&data);
535        for _ in 0..100 {
536            let items = obj.as_array().expect("nested array").to_vec();
537            assert_eq!(items.len(), 1);
538            obj = items.into_iter().next().unwrap();
539        }
540        assert_eq!(obj, Object::Int(7));
541    }
542}