Skip to main content

structured_pades/document/
catalogue.rs

1//! Physical census and indexed compressed signatures use the same value parser.
2
3use super::{Document, Reading};
4use crate::arena::{Arena, Kind};
5use crate::error::{Error, ErrorKind, ReadFailure, Syntax};
6use crate::model::{admit, get, named, required, unsigned};
7use crate::object::Value;
8use crate::range::RangeSource;
9use crate::signature::{Catalogue, inspect, inspect_part};
10use crate::syntax::{Cursor, read_into};
11use crate::xref::Target;
12use structured_asn1::memory::{OwnedVec, SortError, Work};
13
14struct Census<'a, 's, D, G, W: Work> {
15    readings: &'a mut OwnedVec<Reading<'s, D, G>, W>,
16    signatures: &'a mut OwnedVec<crate::signature::Entry, W>,
17    failures: &'a mut OwnedVec<crate::signature::Failure, W>,
18    order: &'a mut OwnedVec<crate::signature::Position, W>,
19    definitions: &'a mut OwnedVec<crate::signature::Definition, W>,
20}
21
22#[derive(Clone, Copy)]
23struct Physical {
24    start: u64,
25    revision: usize,
26    xref_stream: bool,
27}
28
29struct Scan<'a> {
30    table: Option<&'a super::Section>,
31    revision: Option<usize>,
32    ignored_footer: Option<u64>,
33    xref_stream: bool,
34}
35
36struct Expected {
37    offset: u64,
38    number: u64,
39    generation: u16,
40    member: Option<(u64, u64)>,
41    location: crate::source::Location,
42    found: bool,
43}
44
45impl<S: RangeSource> Document<'_, S> {
46    fn expected<W: Work>(&self, work: &W) -> crate::Result<OwnedVec<Expected, W>> {
47        let mut expected = work.vector();
48        for entry in &self.entries {
49            admit(work, entry.location, 1)?;
50            let generation = match entry.target {
51                Target::File { generation, .. } => generation,
52                Target::Compressed { .. } => 0,
53                _ => continue,
54            };
55            if self.target(entry.revision, entry.number, generation, work)? != Some(entry.target) {
56                continue;
57            }
58            let (offset, number, generation, member) = match entry.target {
59                Target::File { offset, generation } => (offset, entry.number, generation, None),
60                Target::Compressed { stream, index } => {
61                    let Some(Target::File {
62                        offset,
63                        generation: 0,
64                    }) = self.target(entry.revision, stream, 0, work)?
65                    else {
66                        return Err(Error {
67                            kind: ErrorKind::Syntax(Syntax::CrossReference),
68                            location: entry.location,
69                        });
70                    };
71                    (offset, stream, 0, Some((index, entry.number)))
72                }
73                _ => unreachable!("only selected file/compressed rows"),
74            };
75            expected
76                .try_push(Expected {
77                    offset,
78                    number,
79                    generation,
80                    member,
81                    location: entry.location,
82                    found: false,
83                })
84                .map_err(|cause| Error {
85                    kind: ErrorKind::Resource(cause),
86                    location: entry.location,
87                })?;
88        }
89        expected
90            .try_sort_unstable_by(|a, b| {
91                admit(work, b.location, 1)?;
92                Ok::<_, Error>(a.offset.cmp(&b.offset))
93            })
94            .map_err(|cause| match cause {
95                SortError::Comparison(cause) => cause,
96                SortError::Resource(cause) => Error {
97                    kind: ErrorKind::Resource(cause),
98                    location: self.arena.nodes[self.history[0].trailer.0].location,
99                },
100            })?;
101        Ok(expected)
102    }
103}
104
105fn first_at(expected: &[Expected], offset: u64, work: &impl Work) -> crate::Result<usize> {
106    let mut low = 0;
107    let mut high = expected.len();
108    while low < high {
109        let middle = low + (high - low) / 2;
110        admit(work, expected[middle].location, 1)?;
111        if expected[middle].offset < offset {
112            low = middle + 1;
113        } else {
114            high = middle;
115        }
116    }
117    Ok(low)
118}
119
120fn check_expected(
121    expected: &mut [Expected],
122    identity: (u64, u16),
123    members: Option<&[super::members::Member]>,
124    start: u64,
125    work: &impl Work,
126) -> crate::Result<()> {
127    let mut entry = first_at(expected, start, work)?;
128    while entry < expected.len() {
129        admit(work, expected[entry].location, 1)?;
130        if expected[entry].offset != start {
131            break;
132        }
133        let item = &mut expected[entry];
134        if item.number != identity.0 || item.generation != identity.1 {
135            return Err(Error {
136                kind: ErrorKind::Syntax(Syntax::CrossReference),
137                location: item.location,
138            });
139        }
140        if let Some((index, number)) = item.member {
141            let index = usize::try_from(index).map_err(|_| Error {
142                kind: ErrorKind::Syntax(Syntax::CrossReference),
143                location: item.location,
144            })?;
145            if members.is_none_or(|members| {
146                members
147                    .get(index)
148                    .is_none_or(|member| member.number != number)
149            }) {
150                return Err(Error {
151                    kind: ErrorKind::Syntax(Syntax::CrossReference),
152                    location: item.location,
153                });
154            }
155        }
156        item.found = true;
157        entry += 1;
158    }
159    Ok(())
160}
161
162impl<'s, S: RangeSource> Document<'s, S> {
163    async fn physical_reading(
164        &self,
165        cursor: &mut Cursor<'_, S>,
166        revision: usize,
167        work: &impl Work,
168    ) -> Result<Reading<'s, S::Document, S::Generation>, ReadFailure<S::Error>> {
169        let start = cursor.at;
170        let (arena, indirect) = crate::stream::open(cursor, Arena::new(work).into_owned()).await?;
171        let mut stream = None;
172        if let Some(body) = indirect.stream {
173            let length = required(&arena, indirect.value, b"Length", work)?;
174            let length = match arena.nodes[length.0].kind {
175                Kind::Reference { number, generation } => {
176                    let value = self
177                        .object(revision, number, generation, work)
178                        .await?
179                        .ok_or_else(|| cursor.failure(Syntax::Stream, body))?;
180                    value.value.root().unsigned(work)?
181                }
182                _ => unsigned(&arena, length, work)?,
183            };
184            stream = Some(crate::stream::close(cursor, body, length).await?);
185        }
186        if cursor.at > self.history[revision].facts.end {
187            return Err(cursor.failure(Syntax::Object, start));
188        }
189        let reading = Reading {
190            value: Value {
191                arena,
192                root: indirect.value,
193                session: self.session,
194                stamp: self.stamp,
195                end: cursor.at,
196            },
197            decoded: None,
198            stream,
199            number: indirect.number,
200            generation: indirect.generation,
201            revision,
202        };
203        Ok(reading)
204    }
205
206    fn inspect_members<W: Work>(
207        &self,
208        group: Reading<'s, S::Document, S::Generation>,
209        members: &[super::members::Member],
210        location: crate::source::Location,
211        census: Census<'_, 's, S::Document, S::Generation, W>,
212        work: &impl Work,
213    ) -> crate::Result<()> {
214        let Census {
215            readings,
216            signatures,
217            failures,
218            order,
219            definitions,
220        } = census;
221        let revision = group.revision;
222        let before = order.len();
223        for member in members {
224            admit(work, location, 1)?;
225            let location = group.value.arena.nodes[member.root.0].location;
226            definitions
227                .try_push(crate::signature::Definition {
228                    identity: (member.number, 0),
229                    location,
230                    encoded: location,
231                    revision,
232                    kind: crate::signature::DefinitionKind::Object,
233                })
234                .map_err(|cause| Error {
235                    kind: ErrorKind::Resource(cause),
236                    location,
237                })?;
238            inspect_part(
239                self,
240                &group,
241                readings.len(),
242                member.nodes.clone(),
243                (member.number, 0),
244                crate::signature::Inspection {
245                    entries: signatures,
246                    failures,
247                    order,
248                },
249                work,
250            )?;
251        }
252        if order.len() != before {
253            readings.try_push(group).map_err(|cause| Error {
254                kind: ErrorKind::Resource(cause),
255                location,
256            })?;
257        }
258        Ok(())
259    }
260
261    async fn census_object<W: Work>(
262        &self,
263        cursor: &mut Cursor<'_, S>,
264        point: Physical,
265        census: Census<'_, 's, S::Document, S::Generation, W>,
266        expected: &mut [Expected],
267        work: &impl Work,
268    ) -> Result<(), ReadFailure<S::Error>> {
269        let Physical {
270            start,
271            revision,
272            xref_stream,
273        } = point;
274        let Census {
275            readings,
276            signatures,
277            failures,
278            order,
279            definitions,
280        } = census;
281        let reading = self.physical_reading(cursor, revision, work).await?;
282        let mut object_stream = false;
283        if reading.stream.is_some()
284            && let Some(kind) = get(&reading.value.arena, reading.value.root, b"Type", work)?
285        {
286            object_stream = if let Kind::Reference { number, generation } =
287                reading.value.arena.nodes[kind.0].kind
288            {
289                let kind = self.object(revision, number, generation, work).await?;
290                if let Some(kind) = kind {
291                    kind.value.root().name_equals(b"ObjStm", work)?
292                } else {
293                    false
294                }
295            } else {
296                named(&reading.value.arena, kind, b"ObjStm", work)?
297            };
298        }
299        let members = if object_stream {
300            self.all_members(&reading, work).await?
301        } else {
302            None
303        };
304        definitions
305            .try_push(crate::signature::Definition {
306                identity: (reading.number, reading.generation),
307                location: reading.value.root().location(),
308                encoded: cursor.location(start, cursor.at - start),
309                revision,
310                kind: if xref_stream {
311                    crate::signature::DefinitionKind::CrossReference
312                } else if object_stream {
313                    crate::signature::DefinitionKind::ObjectStream
314                } else {
315                    crate::signature::DefinitionKind::Object
316                },
317            })
318            .map_err(|cause| Error {
319                kind: ErrorKind::Resource(cause),
320                location: cursor.location(start, 0),
321            })?;
322        check_expected(
323            expected,
324            (reading.number, reading.generation),
325            members.as_ref().map(|(_, values)| &values[..]),
326            start,
327            work,
328        )?;
329        let before = order.len();
330        inspect(
331            self,
332            &reading,
333            readings.len(),
334            signatures,
335            failures,
336            order,
337            work,
338        )?;
339        if order.len() != before {
340            readings.try_push(reading).map_err(|cause| Error {
341                kind: ErrorKind::Resource(cause),
342                location: cursor.location(start, 0),
343            })?;
344        }
345        if let Some((group, members)) = members {
346            self.inspect_members(
347                group,
348                &members,
349                cursor.location(start, 0),
350                Census {
351                    readings,
352                    signatures,
353                    failures,
354                    order,
355                    definitions,
356                },
357                work,
358            )?;
359        }
360        Ok(())
361    }
362
363    fn scan_point(
364        &self,
365        start: u64,
366        location: crate::source::Location,
367        seen: &mut [bool],
368        work: &impl Work,
369    ) -> crate::Result<Scan<'_>> {
370        let mut table = None;
371        let mut revision = None;
372        let mut ignored_footer = None;
373        let mut xref_stream = false;
374        for (ordinal, section) in self.sections.iter().enumerate() {
375            admit(work, location, 1)?;
376            if section.offset == start {
377                seen[ordinal] = true;
378                if section.table {
379                    table = Some(section);
380                } else {
381                    xref_stream = true;
382                }
383            }
384            if let Some((at, end)) = section.ignored_footer
385                && at == start
386            {
387                ignored_footer = Some(end);
388            }
389        }
390        for (ordinal, historical) in self.history.iter().enumerate() {
391            admit(work, location, 1)?;
392            if revision.is_none() && start < historical.facts.end {
393                revision = Some(ordinal);
394            }
395        }
396        Ok(Scan {
397            table,
398            revision,
399            ignored_footer,
400            xref_stream,
401        })
402    }
403
404    async fn census_trailer<W: Work>(
405        &self,
406        cursor: &mut Cursor<'_, S>,
407        section: &super::Section,
408        revision: Option<usize>,
409        start: u64,
410        census: Census<'_, 's, S::Document, S::Generation, W>,
411        work: &impl Work,
412    ) -> Result<(), ReadFailure<S::Error>> {
413        let Census {
414            readings,
415            signatures,
416            failures,
417            order,
418            definitions: _,
419        } = census;
420        cursor.at = section.trailer.span().start;
421        let (arena, root) = read_into(cursor, self.session, Arena::new(work).into_owned()).await?;
422        if cursor.at != section.end {
423            return Err(cursor.failure(Syntax::CrossReference, cursor.at));
424        }
425        let reading = Reading {
426            value: Value {
427                arena,
428                root,
429                session: self.session,
430                stamp: self.stamp,
431                end: cursor.at,
432            },
433            decoded: None,
434            stream: None,
435            number: 0,
436            generation: 0,
437            revision: revision.ok_or_else(|| cursor.failure(Syntax::Trailer, start))?,
438        };
439        let before = order.len();
440        inspect(
441            self,
442            &reading,
443            readings.len(),
444            signatures,
445            failures,
446            order,
447            work,
448        )?;
449        if order.len() != before {
450            readings.try_push(reading).map_err(|cause| Error {
451                kind: ErrorKind::Resource(cause),
452                location: cursor.location(start, 0),
453            })?;
454        }
455        Ok(())
456    }
457
458    fn check_census(
459        &self,
460        cursor: &Cursor<'_, S>,
461        seen: &[bool],
462        expected: &[Expected],
463        work: &impl Work,
464    ) -> Result<(), ReadFailure<S::Error>> {
465        for (ordinal, &encountered) in seen.iter().enumerate() {
466            admit(work, cursor.location(self.sections[ordinal].offset, 0), 1)?;
467            if !encountered {
468                return Err(cursor.failure(Syntax::CrossReference, self.sections[ordinal].offset));
469            }
470        }
471        for item in expected {
472            admit(work, item.location, 1)?;
473            if !item.found {
474                return Err(Error {
475                    kind: ErrorKind::Syntax(Syntax::CrossReference),
476                    location: item.location,
477                }
478                .into());
479            }
480        }
481        Ok(())
482    }
483
484    /// Enumerates physical signature dictionaries, including definitions not
485    /// selected by the latest xref, and historically indexed compressed values.
486    /// Stream payload bytes are skipped by their resolved Length, never scanned
487    /// for apparent `obj` or signature text inside binary/string content.
488    pub async fn catalogue<W: Work>(
489        &self,
490        operation: &W,
491    ) -> Result<Catalogue<'s, S::Document, S::Generation>, ReadFailure<S::Error>> {
492        let reader = self.original_reader(operation)?;
493        let work = reader.execution();
494        let mut cursor = Cursor { reader, at: 0 };
495        let mut readings = work.vector();
496        let mut signatures = work.vector();
497        let mut failures = work.vector();
498        let mut order = work.vector();
499        let mut definitions = work.vector();
500        let mut expected = self.expected(&work)?;
501        let mut seen = work.vector();
502        for _ in &self.sections {
503            seen.try_push(false).map_err(|cause| Error {
504                kind: ErrorKind::Resource(cause),
505                location: cursor.location(0, 0),
506            })?;
507        }
508        while cursor.at < self.stamp.length {
509            cursor.skip().await?;
510            if cursor.peek().await?.is_none() {
511                break;
512            }
513            let start = cursor.at;
514            let Scan {
515                table,
516                revision,
517                ignored_footer,
518                xref_stream,
519            } = self.scan_point(start, cursor.location(start, 0), &mut seen, &work)?;
520            if let Some(end) = ignored_footer {
521                cursor.at = end;
522                continue;
523            }
524            if let Some(section) = table {
525                self.census_trailer(
526                    &mut cursor,
527                    section,
528                    revision,
529                    start,
530                    Census {
531                        readings: &mut readings,
532                        signatures: &mut signatures,
533                        failures: &mut failures,
534                        order: &mut order,
535                        definitions: &mut definitions,
536                    },
537                    &work,
538                )
539                .await?;
540                continue;
541            }
542            if cursor.looking(b"startxref").await? {
543                cursor.keyword(b"startxref").await?;
544                cursor.space().await?;
545                let offset = cursor.natural().await?;
546                let end = super::finish_footer(&mut cursor).await?;
547                let mut found = false;
548                for historical in &self.history {
549                    admit(&work, cursor.location(start, 0), 1)?;
550                    if historical.facts.xref == offset && historical.facts.end == end {
551                        found = true;
552                        break;
553                    }
554                }
555                if !found {
556                    return Err(cursor.failure(Syntax::StartXref, start));
557                }
558                continue;
559            }
560            let revision = revision.ok_or_else(|| cursor.failure(Syntax::Object, start))?;
561            self.census_object(
562                &mut cursor,
563                Physical {
564                    start,
565                    revision,
566                    xref_stream,
567                },
568                Census {
569                    readings: &mut readings,
570                    signatures: &mut signatures,
571                    failures: &mut failures,
572                    order: &mut order,
573                    definitions: &mut definitions,
574                },
575                &mut expected,
576                &work,
577            )
578            .await?;
579        }
580        self.check_census(&cursor, &seen, &expected, &work)?;
581        cursor.reader.check(cursor.location(0, self.stamp.length))?;
582        Ok(Catalogue {
583            readings: readings.into_owned(),
584            entries: signatures.into_owned(),
585            failures: failures.into_owned(),
586            order: order.into_owned(),
587            definitions: definitions.into_owned(),
588        })
589    }
590}