Skip to main content

structured_pades/document/
resolve.rs

1//! Iterative dependency frames for stream lengths and compressed members.
2
3use super::Document;
4use crate::arena::{Arena, Kind, NodeId};
5use crate::error::{Error, ErrorKind, ReadFailure, Syntax};
6use crate::model::{admit, required, unsigned};
7use crate::object::Value;
8use crate::range::{RangeReader, RangeSource};
9use crate::source::Location;
10use crate::stream::Indirect;
11use crate::syntax::Cursor;
12use crate::xref::Target;
13use structured_asn1::{
14    Span,
15    memory::{Memory, OwnedVec, Work},
16};
17
18/// Independently retained object metadata and any decoded source storage.
19/// Encoded file strings remain ranges in the immutable original document.
20pub struct Reading<'s, D, G> {
21    pub(crate) value: Value<'s, D, G>,
22    pub(crate) decoded: Option<OwnedVec<u8, Memory>>,
23    pub(crate) stream: Option<Span>,
24    pub(crate) number: u64,
25    pub(crate) generation: u16,
26    pub(crate) revision: usize,
27}
28
29#[expect(
30    clippy::large_enum_variant,
31    reason = "direct values borrow their owner while indirect values transfer their existing funded reading without an additional box"
32)]
33pub(crate) enum ResolvedValue<'a, 's, D, G> {
34    Borrowed {
35        reading: &'a Reading<'s, D, G>,
36        node: NodeId,
37    },
38    Owned(Reading<'s, D, G>),
39}
40impl<'s, D, G> ResolvedValue<'_, 's, D, G> {
41    pub(crate) fn parts(&self) -> (&Reading<'s, D, G>, NodeId) {
42        match self {
43            Self::Borrowed { reading, node } => (reading, *node),
44            Self::Owned(reading) => (reading, reading.value.root),
45        }
46    }
47}
48
49impl<D: Copy + Eq, G: Copy + Eq> Reading<'_, D, G> {
50    /// Parsed original value, without a signature or trust assertion.
51    #[must_use]
52    pub fn value(&self) -> &Value<'_, D, G> {
53        &self.value
54    }
55
56    /// Original indirect object number and generation selected by the index.
57    #[must_use]
58    pub fn identity(&self) -> (u64, u16) {
59        (self.number, self.generation)
60    }
61
62    /// Historical resolution context for this reading.
63    #[must_use]
64    pub fn revision(&self) -> usize {
65        self.revision
66    }
67
68    /// Encoded stream body in the physical file, excluding delimiters.
69    #[must_use]
70    pub fn stream(&self) -> Option<Span> {
71        self.stream
72    }
73}
74
75struct Pending<'s, D, G> {
76    reading: Reading<'s, D, G>,
77    indirect: Indirect,
78}
79
80#[expect(
81    clippy::large_enum_variant,
82    reason = "one funded iterative frame owns its suspended parsing state; per-state boxes would add allocation and rollback boundaries"
83)]
84enum State<'b, 's, D, G> {
85    Start,
86    Length(Pending<'s, D, G>),
87    Member {
88        index: u64,
89    },
90    Controls {
91        controls: super::controls::Controls<'b, 's, D, G>,
92        member: Option<(u64, u64)>,
93    },
94    Reference,
95}
96
97struct Frame<'b, 's, D, G> {
98    number: u64,
99    generation: u16,
100    state: State<'b, 's, D, G>,
101}
102
103struct Completion<'s, D, G> {
104    object: Option<Reading<'s, D, G>>,
105}
106impl<'s, D, G> Completion<'s, D, G> {
107    fn present(value: Reading<'s, D, G>) -> Self {
108        Self {
109            object: Some(value),
110        }
111    }
112}
113
114struct Resolution<'a, 'q, 'b, 's, S: RangeSource, W: Work> {
115    reader: &'a mut RangeReader<'q, S>,
116    frames: &'a mut OwnedVec<Frame<'b, 's, S::Document, S::Generation>, W>,
117    completed: &'a mut Option<Completion<'s, S::Document, S::Generation>>,
118}
119
120#[derive(Clone, Copy)]
121enum Request<'b, 's, D, G> {
122    Object {
123        number: u64,
124        generation: u16,
125    },
126    Stream {
127        reading: &'b Reading<'s, D, G>,
128        object_stream: bool,
129    },
130}
131#[expect(
132    clippy::large_enum_variant,
133    reason = "the completed iterative resolver transfers an already owned reading without allocating an output envelope"
134)]
135enum Output<'s, D, G> {
136    Object(Option<Reading<'s, D, G>>),
137    Stream(Prepared),
138}
139pub(super) struct Prepared {
140    pub(super) plans: OwnedVec<crate::stream::Step>,
141    pub(super) body: Span,
142    pub(super) layout: Option<(u64, u64)>,
143}
144pub(crate) struct Decoded {
145    pub(crate) bytes: OwnedVec<u8>,
146    pub(crate) body: Span,
147    pub(crate) layout: Option<(u64, u64)>,
148}
149
150impl<'s, S: RangeSource> Document<'s, S> {
151    /// ISO32000-1 ยง7.3.9: defined null and missing indirect targets have the
152    /// same null value. Schema callers decide whether absence is permitted.
153    pub(crate) async fn resolve_value<'a, W: Work>(
154        &self,
155        reading: &'a Reading<'s, S::Document, S::Generation>,
156        node: NodeId,
157        operation: &W,
158    ) -> Result<Option<ResolvedValue<'a, 's, S::Document, S::Generation>>, ReadFailure<S::Error>>
159    {
160        let work = self.session.query(operation);
161        let location = reading.value.root().location();
162        self.check(&work, location)?;
163        if !core::ptr::eq(reading.value.session, self.session) || reading.value.stamp != self.stamp
164        {
165            return Err(Error {
166                kind: ErrorKind::Foreign,
167                location,
168            }
169            .into());
170        }
171        admit(&work, location, 1)?;
172        match reading.value.arena.nodes[node.0].kind {
173            Kind::Null => Ok(None),
174            Kind::Reference { number, generation } => {
175                let Some(value) = self
176                    .object(reading.revision, number, generation, &work)
177                    .await?
178                else {
179                    return Ok(None);
180                };
181                admit(&work, value.value.root().location(), 1)?;
182                if matches!(value.value.arena.nodes[value.value.root.0].kind, Kind::Null) {
183                    return Ok(None);
184                }
185                Ok(Some(ResolvedValue::Owned(value)))
186            }
187            _ => Ok(Some(ResolvedValue::Borrowed { reading, node })),
188        }
189    }
190
191    /// Resolves an indirect identity at a specific revision. Free, unknown,
192    /// generation-mismatched and absent references are PDF null (None).
193    /// References, stream lengths and object-stream dependencies use explicit
194    /// bounded frames; no input-controlled Rust recursion or boxed future.
195    pub async fn object<W: Work>(
196        &self,
197        revision: usize,
198        number: u64,
199        generation: u16,
200        operation: &W,
201    ) -> Result<Option<Reading<'s, S::Document, S::Generation>>, ReadFailure<S::Error>> {
202        match self
203            .resolve(revision, Request::Object { number, generation }, operation)
204            .await?
205        {
206            Output::Object(value) => Ok(value),
207            Output::Stream(_) => unreachable!("object resolution request"),
208        }
209    }
210
211    pub(crate) async fn stream_data<W: Work>(
212        &self,
213        reading: &Reading<'s, S::Document, S::Generation>,
214        object_stream: bool,
215        operation: &W,
216    ) -> Result<Decoded, ReadFailure<S::Error>> {
217        let prepared = self.stream_plan(reading, object_stream, operation).await?;
218        let mut reader = RangeReader::new(self.source, self.session, operation)?;
219        let location = reading.value.root().location();
220        if reader.stamp() != self.stamp {
221            return Err(crate::range::InputError::Changed { location }.into());
222        }
223        let (bytes, _) = crate::stream::execute(
224            &mut reader,
225            prepared.body,
226            &prepared.plans,
227            reading.revision,
228            true,
229        )
230        .await?;
231        reader.check(location)?;
232        Ok(Decoded {
233            bytes,
234            body: prepared.body,
235            layout: prepared.layout,
236        })
237    }
238
239    pub(super) async fn stream_plan<W: Work>(
240        &self,
241        reading: &Reading<'s, S::Document, S::Generation>,
242        object_stream: bool,
243        operation: &W,
244    ) -> Result<Prepared, ReadFailure<S::Error>> {
245        if !core::ptr::eq(reading.value.session, self.session) || reading.value.stamp != self.stamp
246        {
247            return Err(Error {
248                kind: ErrorKind::Foreign,
249                location: reading.value.root().location(),
250            }
251            .into());
252        }
253        match self
254            .resolve(
255                reading.revision,
256                Request::Stream {
257                    reading,
258                    object_stream,
259                },
260                operation,
261            )
262            .await?
263        {
264            Output::Stream(value) => Ok(value),
265            Output::Object(_) => unreachable!("stream resolution request"),
266        }
267    }
268
269    async fn load_file<'b, W: Work, Q: Work>(
270        &self,
271        revision: usize,
272        mut frame: Frame<'b, 's, S::Document, S::Generation>,
273        offset: u64,
274        context: Resolution<'_, '_, 'b, 's, S, W>,
275        work: &Q,
276    ) -> Result<(), ReadFailure<S::Error>> {
277        let Resolution {
278            reader,
279            frames,
280            completed,
281        } = context;
282        let mut cursor = Cursor {
283            reader: reader.fork()?,
284            at: offset,
285        };
286        let (arena, indirect) =
287            crate::stream::open(&mut cursor, Arena::new(work).into_owned()).await?;
288        if indirect.number != frame.number || indirect.generation != frame.generation {
289            return Err(cursor.failure(Syntax::Object, offset));
290        }
291        let value = Value {
292            arena,
293            root: indirect.value,
294            session: self.session,
295            stamp: self.stamp,
296            end: cursor.at,
297        };
298        let reading = Reading {
299            value,
300            decoded: None,
301            stream: None,
302            number: frame.number,
303            generation: frame.generation,
304            revision,
305        };
306        if indirect.stream.is_some() {
307            let length = required(&reading.value.arena, indirect.value, b"Length", work)?;
308            if let Kind::Reference { number, generation } = reading.value.arena.nodes[length.0].kind
309            {
310                frame.state = State::Length(Pending { reading, indirect });
311                suspend(
312                    frames,
313                    frame,
314                    (number, generation),
315                    self,
316                    cursor.location(offset, 0),
317                    work,
318                )?;
319                return Ok(());
320            }
321            let length = unsigned(&reading.value.arena, length, work)?;
322            *completed = Some(Completion::present(
323                finish(&mut cursor, Pending { reading, indirect }, length).await?,
324            ));
325        } else if let Some((number, generation)) = reading.value.root().reference() {
326            frame.state = State::Reference;
327            suspend(
328                frames,
329                frame,
330                (number, generation),
331                self,
332                cursor.location(offset, 0),
333                work,
334            )?;
335            return Ok(());
336        } else {
337            *completed = Some(Completion::present(reading));
338        }
339        Ok(())
340    }
341
342    async fn control<'b, W: Work>(
343        &self,
344        revision: usize,
345        (number, generation): (u64, u16),
346        mut controls: super::controls::Controls<'b, 's, S::Document, S::Generation>,
347        selected: Option<(u64, u64)>,
348        context: Resolution<'_, '_, 'b, 's, S, W>,
349        work: &impl Work,
350    ) -> Result<Option<Prepared>, ReadFailure<S::Error>> {
351        let Resolution {
352            reader,
353            frames,
354            completed,
355        } = context;
356        let mut frame = Frame {
357            number,
358            generation,
359            state: State::Start,
360        };
361        if controls.waiting() {
362            controls.supply(
363                completed
364                    .take()
365                    .expect("completed control dependency")
366                    .object,
367                work,
368            )?;
369        }
370        match controls.advance(work) {
371            Err(crate::stream::plan::Failure::Fault(error)) => return Err(error.into()),
372            Err(crate::stream::plan::Failure::Need { number, generation }) => {
373                frame.state = State::Controls {
374                    controls,
375                    member: selected,
376                };
377                suspend(
378                    frames,
379                    frame,
380                    (number, generation),
381                    self,
382                    reader.location(0, 0),
383                    work,
384                )?;
385                return Ok(None);
386            }
387            Ok(()) => {}
388        }
389        let location = controls.container.value.root().location();
390        let body = controls.container.stream.ok_or(Error {
391            kind: ErrorKind::Syntax(Syntax::Stream),
392            location,
393        })?;
394        let plans = controls.plans();
395        let layout = controls.layout;
396        if let Some((_, selected)) = selected {
397            let (count, _) = layout.expect("object-stream controls");
398            if selected >= count {
399                return Err(Error {
400                    kind: ErrorKind::Syntax(Syntax::Object),
401                    location,
402                }
403                .into());
404            }
405        }
406        if let Some((number, selected)) = selected {
407            let (bytes, _) = crate::stream::execute(reader, body, &plans, revision, true).await?;
408            drop(plans);
409            let (count, first) = layout.expect("object-stream controls");
410            let reading = member(
411                self,
412                reader,
413                bytes,
414                body,
415                (count, first),
416                (number, selected),
417                revision,
418            )
419            .await?;
420            if let Some((number, generation)) = reading.value.root().reference() {
421                frame.state = State::Reference;
422                suspend(frames, frame, (number, generation), self, location, work)?;
423                return Ok(None);
424            }
425            *completed = Some(Completion::present(reading));
426        } else {
427            debug_assert!(frames.is_empty());
428            reader.check(location)?;
429            return Ok(Some(Prepared {
430                plans,
431                body,
432                layout,
433            }));
434        }
435        Ok(None)
436    }
437
438    fn compressed<'b, W: Work>(
439        &self,
440        revision: usize,
441        mut frame: Frame<'b, 's, S::Document, S::Generation>,
442        (stream, index): (u64, u64),
443        frames: &mut OwnedVec<Frame<'b, 's, S::Document, S::Generation>, W>,
444        location: Location,
445        work: &impl Work,
446    ) -> crate::Result<()> {
447        if !matches!(
448            self.target(revision, stream, 0, work)?,
449            Some(Target::File { .. })
450        ) {
451            return Err(Error {
452                kind: ErrorKind::Syntax(Syntax::Object),
453                location,
454            });
455        }
456        frame.state = State::Member { index };
457        suspend(frames, frame, (stream, 0), self, location, work)
458    }
459
460    async fn resolve<W: Work>(
461        &self,
462        revision: usize,
463        request: Request<'_, 's, S::Document, S::Generation>,
464        operation: &W,
465    ) -> Result<Output<'s, S::Document, S::Generation>, ReadFailure<S::Error>> {
466        let mut reader = self.original_reader(operation)?;
467        let work = reader.execution();
468        let mut frames = work.vector();
469        let frame = initial(request, &work);
470        push(&mut frames, frame, self, reader.location(0, 0), &work)?;
471        let mut completed = None;
472        while let Some(frame) = frames.pop() {
473            admit(&work, reader.location(0, 0), 1)?;
474            match frame.state {
475                State::Start => {
476                    match self.target(revision, frame.number, frame.generation, &work)? {
477                        None => completed = Some(Completion { object: None }),
478                        Some(Target::File { offset, .. }) => {
479                            self.load_file(
480                                revision,
481                                frame,
482                                offset,
483                                Resolution {
484                                    reader: &mut reader,
485                                    frames: &mut frames,
486                                    completed: &mut completed,
487                                },
488                                &work,
489                            )
490                            .await?;
491                        }
492                        Some(Target::Compressed { stream, index }) => {
493                            self.compressed(
494                                revision,
495                                frame,
496                                (stream, index),
497                                &mut frames,
498                                reader.location(0, 0),
499                                &work,
500                            )?;
501                            continue;
502                        }
503                        Some(Target::Free { .. } | Target::Null) => {
504                            unreachable!("filtered resolver target")
505                        }
506                    }
507                }
508                State::Length(pending) => {
509                    completed = Some(Completion::present(
510                        resolved_length(&reader, pending, completed.take(), &work).await?,
511                    ));
512                }
513                State::Member { index } => {
514                    member_controls(
515                        frame,
516                        index,
517                        Resolution {
518                            reader: &mut reader,
519                            frames: &mut frames,
520                            completed: &mut completed,
521                        },
522                        &work,
523                    )?;
524                    continue;
525                }
526                State::Controls {
527                    controls,
528                    member: selected,
529                } => {
530                    if let Some(prepared) = self
531                        .control(
532                            revision,
533                            (frame.number, frame.generation),
534                            controls,
535                            selected,
536                            Resolution {
537                                reader: &mut reader,
538                                frames: &mut frames,
539                                completed: &mut completed,
540                            },
541                            &work,
542                        )
543                        .await?
544                    {
545                        return Ok(Output::Stream(prepared));
546                    }
547                }
548                State::Reference => {
549                    debug_assert!(completed.is_some());
550                }
551            }
552            if frames.is_empty() {
553                reader.check(reader.location(0, self.stamp.length))?;
554                return Ok(Output::Object(
555                    completed.expect("completed root frame").object,
556                ));
557            }
558        }
559        unreachable!("initial root frame")
560    }
561}
562
563async fn resolved_length<'s, S: RangeSource>(
564    reader: &RangeReader<'_, S>,
565    pending: Pending<'s, S::Document, S::Generation>,
566    completed: Option<Completion<'s, S::Document, S::Generation>>,
567    work: &impl Work,
568) -> Result<Reading<'s, S::Document, S::Generation>, ReadFailure<S::Error>> {
569    let Some(Completion {
570        object: Some(length),
571    }) = completed
572    else {
573        return Err(Error {
574            kind: ErrorKind::Syntax(Syntax::Stream),
575            location: pending.reading.value.root().location(),
576        }
577        .into());
578    };
579    let length = length.value.root().unsigned(work)?;
580    let mut cursor = Cursor {
581        reader: reader.fork()?,
582        at: 0,
583    };
584    finish(&mut cursor, pending, length).await
585}
586
587fn member_controls<'b, 's, S: RangeSource, W: Work>(
588    mut frame: Frame<'b, 's, S::Document, S::Generation>,
589    index: u64,
590    context: Resolution<'_, '_, 'b, 's, S, W>,
591    work: &impl Work,
592) -> Result<(), ReadFailure<S::Error>> {
593    let Resolution {
594        reader,
595        frames,
596        completed,
597    } = context;
598    let Some(Completion {
599        object: Some(container),
600    }) = completed.take()
601    else {
602        return Err(Error {
603            kind: ErrorKind::Syntax(Syntax::Object),
604            location: reader.location(0, 0),
605        }
606        .into());
607    };
608    frame.state = State::Controls {
609        controls: super::controls::Controls::new(
610            super::controls::Owner::Owned(container),
611            true,
612            work,
613        ),
614        member: Some((frame.number, index)),
615    };
616    frames
617        .try_push(frame)
618        .map_err(|cause| resource(cause, reader.location(0, 0)))?;
619    Ok(())
620}
621
622fn initial<'b, 's, D: Copy + Eq, G: Copy + Eq>(
623    request: Request<'b, 's, D, G>,
624    work: &impl Work,
625) -> Frame<'b, 's, D, G> {
626    match request {
627        Request::Object { number, generation } => Frame {
628            number,
629            generation,
630            state: State::Start,
631        },
632        Request::Stream {
633            reading,
634            object_stream,
635        } => Frame {
636            number: reading.number,
637            generation: reading.generation,
638            state: State::Controls {
639                controls: super::controls::Controls::new(
640                    super::controls::Owner::Borrowed(reading),
641                    object_stream,
642                    work,
643                ),
644                member: None,
645            },
646        },
647    }
648}
649
650fn suspend<'b, 's, S: RangeSource, W: Work>(
651    frames: &mut OwnedVec<Frame<'b, 's, S::Document, S::Generation>, W>,
652    frame: Frame<'b, 's, S::Document, S::Generation>,
653    (number, generation): (u64, u16),
654    document: &Document<'_, S>,
655    location: Location,
656    work: &impl Work,
657) -> crate::Result<()> {
658    frames
659        .try_push(frame)
660        .map_err(|cause| resource(cause, location))?;
661    push(
662        frames,
663        Frame {
664            number,
665            generation,
666            state: State::Start,
667        },
668        document,
669        location,
670        work,
671    )
672}
673
674fn resource(cause: structured_asn1::memory::MemoryError, location: Location) -> Error {
675    Error {
676        kind: ErrorKind::Resource(cause),
677        location,
678    }
679}
680
681fn push<'b, 's, S: RangeSource, W: Work>(
682    frames: &mut OwnedVec<Frame<'b, 's, S::Document, S::Generation>, W>,
683    frame: Frame<'b, 's, S::Document, S::Generation>,
684    document: &Document<'_, S>,
685    location: Location,
686    work: &impl Work,
687) -> crate::Result<()> {
688    // Iterative frames also obey a fixed safety ceiling, independently of a
689    // caller accidentally selecting an unbounded nesting allowance.
690    if frames.len() >= 512
691        || frames.len() as u64 >= u64::from(document.session.limits().processing.max_depth)
692    {
693        return Err(Error {
694            kind: ErrorKind::Limit(crate::error::Limit::Depth),
695            location,
696        });
697    }
698    for prior in frames.iter() {
699        admit(work, location, 1)?;
700        if prior.number == frame.number && prior.generation == frame.generation {
701            return Err(Error {
702                kind: ErrorKind::Syntax(Syntax::Cycle),
703                location,
704            });
705        }
706    }
707    frames
708        .try_push(frame)
709        .map_err(|cause| resource(cause, location))
710}
711
712async fn finish<'s, S: RangeSource>(
713    cursor: &mut Cursor<'_, S>,
714    mut pending: Pending<'s, S::Document, S::Generation>,
715    length: u64,
716) -> Result<Reading<'s, S::Document, S::Generation>, ReadFailure<S::Error>> {
717    let body = crate::stream::close(
718        cursor,
719        pending.indirect.stream.expect("pending stream"),
720        length,
721    )
722    .await?;
723    pending.reading.stream = Some(body);
724    pending.reading.value.end = cursor.at;
725    Ok(pending.reading)
726}
727
728async fn member<'s, S: RangeSource>(
729    document: &Document<'s, S>,
730    reader: &mut RangeReader<'_, S>,
731    decoded: OwnedVec<u8>,
732    body: Span,
733    (count, first): (u64, u64),
734    (number, selected): (u64, u64),
735    revision: usize,
736) -> Result<Reading<'s, S::Document, S::Generation>, ReadFailure<S::Error>> {
737    let work = reader.execution();
738    let location = reader.location(body.start, body.length);
739    if selected >= count || count > first / 4 {
740        return Err(Error {
741            kind: ErrorKind::Syntax(Syntax::Object),
742            location,
743        }
744        .into());
745    }
746    let slots = super::members::directory(
747        document.session,
748        document.stamp,
749        &decoded,
750        body,
751        (count, first),
752        revision,
753        &work,
754    )
755    .await
756    .map_err(convert)?;
757    let ordinal = usize::try_from(selected).expect("parsed object-stream directory length");
758    let slot = &slots[ordinal];
759    if slot.number != number {
760        return Err(Error {
761            kind: ErrorKind::Syntax(Syntax::Object),
762            location,
763        }
764        .into());
765    }
766    let value = super::members::selected(
767        document.session,
768        document.stamp,
769        &decoded,
770        body,
771        slot,
772        revision,
773        &work,
774    )
775    .await
776    .map_err(convert)?;
777    Ok(Reading {
778        value,
779        decoded: Some(decoded),
780        stream: None,
781        number,
782        generation: 0,
783        revision,
784    })
785}
786
787pub(crate) fn convert<E>(error: ReadFailure<core::convert::Infallible>) -> ReadFailure<E> {
788    match error {
789        ReadFailure::Format(error) => ReadFailure::Format(error),
790        ReadFailure::Input(error) => ReadFailure::Input(match error {
791            crate::range::InputError::Resource { location, cause } => {
792                crate::range::InputError::Resource { location, cause }
793            }
794            crate::range::InputError::Changed { location } => {
795                crate::range::InputError::Changed { location }
796            }
797            crate::range::InputError::Contract { location } => {
798                crate::range::InputError::Contract { location }
799            }
800            crate::range::InputError::Cause { cause, .. } => match cause {},
801        }),
802    }
803}