1mod catalogue;
4mod compare;
5mod controls;
6mod graph;
7mod members;
8pub(crate) mod resolve;
9#[cfg(test)]
10mod tests;
11pub(crate) use graph::Key as GraphKey;
12pub use graph::{Graph, GraphMembers, GraphObject};
13pub use resolve::Reading;
14
15use crate::Session;
16use crate::arena::{Arena, NodeId};
17use crate::error::{Error, ErrorKind, ReadFailure, Syntax};
18use crate::model::{admit, get, unsigned};
19use crate::range::{RangeReader, RangeSource, Stamp};
20use crate::source::{Location, Source};
21use crate::syntax::Cursor;
22use crate::xref::{Entry, Target};
23use structured_asn1::memory::{Memory, OwnedVec, SortError, Work};
24
25#[derive(Debug, Clone, Copy)]
27pub struct Revision {
28 pub xref: u64,
30 pub end: u64,
32}
33
34struct Historical {
35 facts: Revision,
36 trailer: NodeId,
37}
38
39struct Section {
40 offset: u64,
41 end: u64,
42 trailer: crate::source::Location,
43 table: bool,
44 ignored_footer: Option<(u64, u64)>,
45}
46
47pub struct Document<'s, S: RangeSource> {
50 pub(crate) source: &'s S,
51 pub(crate) session: &'s Session,
52 pub(crate) stamp: Stamp<S::Document, S::Generation>,
53 arena: Arena<Memory>,
54 history: OwnedVec<Historical, Memory>,
55 entries: OwnedVec<Entry, Memory>,
56 sections: OwnedVec<Section, Memory>,
57}
58
59#[derive(Clone, Copy)]
60struct Forward {
61 main: u64,
62 upper: u64,
63 start_entries: usize,
64 section_end: u64,
65 primary: usize,
66 revision: usize,
67 location: Location,
68}
69struct Records<'a, W: Work> {
70 entries: &'a mut OwnedVec<Entry, W>,
71 sections: &'a mut OwnedVec<Section, W>,
72 lengths: &'a mut OwnedVec<crate::xref::DeferredLength, W>,
73}
74impl<W: Work> Records<'_, W> {
75 fn reborrow(&mut self) -> Records<'_, W> {
76 Records {
77 entries: self.entries,
78 sections: self.sections,
79 lengths: self.lengths,
80 }
81 }
82}
83
84async fn forward<S: RangeSource, W: Work>(
85 cursor: &mut Cursor<'_, S>,
86 mut arena: Arena<Memory>,
87 records: Records<'_, W>,
88 context: Forward,
89 work: &impl Work,
90) -> Result<(Arena<Memory>, u64), ReadFailure<S::Error>> {
91 let Forward {
92 main,
93 upper,
94 start_entries,
95 section_end,
96 primary,
97 revision,
98 location,
99 } = context;
100 let Records {
101 entries,
102 sections,
103 lengths,
104 } = records;
105 if main >= upper {
109 return Err(cursor.failure(Syntax::CrossReference, main));
110 }
111 for entry in &mut entries[start_entries..] {
112 admit(work, entry.location, 1)?;
113 entry.priority += 2;
114 }
115 cursor.at = section_end;
116 cursor.skip().await?;
117 if cursor.looking(b"startxref").await? {
118 let start = cursor.at;
119 cursor.keyword(b"startxref").await?;
120 cursor.space().await?;
121 cursor.natural().await?;
122 let end = finish_footer(cursor).await?;
123 sections[primary].ignored_footer = Some((start, end));
124 }
125 cursor.at = main;
126 let table = cursor.looking(b"xref").await?;
127 let (next, main_section) = if table {
128 crate::xref::table(cursor, arena, entries, revision, 0).await?
129 } else {
130 crate::xref::stream(cursor, arena, entries, revision, 0).await?
131 };
132 arena = next;
133 if main_section.previous.is_some() {
134 return Err(cursor.failure(Syntax::Cycle, main));
135 }
136 sections
137 .try_push(Section {
138 offset: main,
139 end: main_section.end,
140 trailer: arena.nodes[main_section.trailer.0].location,
141 table,
142 ignored_footer: None,
143 })
144 .map_err(|cause| Error {
145 kind: ErrorKind::Resource(cause),
146 location,
147 })?;
148 if let Some(length) = main_section.length {
149 lengths.try_push(length).map_err(|cause| Error {
150 kind: ErrorKind::Resource(cause),
151 location: length.location,
152 })?;
153 }
154 if let Some(hybrid) = main_section.hybrid {
155 if hybrid >= upper {
156 return Err(cursor.failure(Syntax::CrossReference, hybrid));
157 }
158 cursor.at = hybrid;
159 let (next, supplement) = crate::xref::stream(cursor, arena, entries, revision, 1).await?;
160 arena = next;
161 if supplement.hybrid.is_some() {
162 return Err(cursor.failure(Syntax::CrossReference, hybrid));
163 }
164 sections
165 .try_push(Section {
166 offset: hybrid,
167 end: supplement.end,
168 trailer: arena.nodes[supplement.trailer.0].location,
169 table: false,
170 ignored_footer: None,
171 })
172 .map_err(|cause| Error {
173 kind: ErrorKind::Resource(cause),
174 location,
175 })?;
176 if let Some(length) = supplement.length {
177 lengths.try_push(length).map_err(|cause| Error {
178 kind: ErrorKind::Resource(cause),
179 location: length.location,
180 })?;
181 }
182 }
183 Ok((arena, main_section.end))
184}
185
186#[derive(Clone, Copy)]
187struct Supplement {
188 hybrid: u64,
189 upper: u64,
190 revision: usize,
191 location: Location,
192}
193
194async fn supplement<S: RangeSource, W: Work>(
195 cursor: &mut Cursor<'_, S>,
196 mut arena: Arena<Memory>,
197 records: Records<'_, W>,
198 context: Supplement,
199) -> Result<Arena<Memory>, ReadFailure<S::Error>> {
200 let Supplement {
201 hybrid,
202 upper,
203 revision,
204 location,
205 } = context;
206 let Records {
207 entries,
208 sections,
209 lengths,
210 } = records;
211 if hybrid >= upper {
212 return Err(cursor.failure(Syntax::CrossReference, hybrid));
213 }
214 cursor.at = hybrid;
215 let (next, supplemental) = crate::xref::stream(cursor, arena, entries, revision, 1).await?;
216 arena = next;
217 sections
218 .try_push(Section {
219 offset: hybrid,
220 end: supplemental.end,
221 trailer: arena.nodes[supplemental.trailer.0].location,
222 table: false,
223 ignored_footer: None,
224 })
225 .map_err(|cause| Error {
226 kind: ErrorKind::Resource(cause),
227 location,
228 })?;
229 if let Some(length) = supplemental.length {
230 lengths.try_push(length).map_err(|cause| Error {
231 kind: ErrorKind::Resource(cause),
232 location: length.location,
233 })?;
234 }
235 if supplemental.hybrid.is_some() {
238 return Err(cursor.failure(Syntax::CrossReference, hybrid));
239 }
240 Ok(arena)
241}
242
243fn save_section<W: Work>(
244 sections: &mut OwnedVec<Section, W>,
245 lengths: &mut OwnedVec<crate::xref::DeferredLength, W>,
246 arena: &Arena<Memory>,
247 section: crate::xref::Section,
248 (offset, table): (u64, bool),
249 location: Location,
250) -> crate::Result<()> {
251 sections
252 .try_push(Section {
253 offset,
254 end: section.end,
255 trailer: arena.nodes[section.trailer.0].location,
256 table,
257 ignored_footer: None,
258 })
259 .map_err(|cause| Error {
260 kind: ErrorKind::Resource(cause),
261 location,
262 })?;
263 if let Some(length) = section.length {
264 lengths.try_push(length).map_err(|cause| Error {
265 kind: ErrorKind::Resource(cause),
266 location: length.location,
267 })?;
268 }
269 Ok(())
270}
271
272struct RevisionTail {
273 offset: u64,
274 final_section_end: u64,
275 final_end: Option<u64>,
276 trailer: NodeId,
277}
278
279async fn revision_tail<S: RangeSource>(
280 cursor: &mut Cursor<'_, S>,
281 arena: &Arena<Memory>,
282 sections: &[Section],
283 tail: RevisionTail,
284 work: &impl Work,
285) -> Result<Historical, ReadFailure<S::Error>> {
286 let RevisionTail {
287 offset,
288 final_section_end,
289 final_end,
290 trailer,
291 } = tail;
292 let location = cursor.location(offset, 0);
293 cursor.at = final_section_end;
294 let end = footer(cursor, offset).await?;
295 for section in sections {
296 admit(work, section.trailer, 1)?;
297 if section.end > end {
298 return Err(cursor.failure(Syntax::CrossReference, section.offset));
299 }
300 }
301 if final_end.is_some_and(|expected| end != expected) {
302 return Err(cursor.failure(Syntax::StartXref, end));
303 }
304 if get(arena, trailer, b"Encrypt", work)?.is_some() {
305 return Err(Error {
306 kind: ErrorKind::Unsupported(crate::error::Unsupported::Encryption),
307 location,
308 }
309 .into());
310 }
311 Ok(Historical {
312 facts: Revision { xref: offset, end },
313 trailer,
314 })
315}
316
317struct Primary {
318 offset: u64,
319 upper: u64,
320 start_entries: usize,
321 section_index: usize,
322 revision: usize,
323 location: Location,
324}
325struct Advanced {
326 arena: Arena<Memory>,
327 historical: Historical,
328 forward: Option<u64>,
329}
330
331async fn advance_revision<S: RangeSource, W: Work>(
332 cursor: &mut Cursor<'_, S>,
333 mut arena: Arena<Memory>,
334 records: Records<'_, W>,
335 context: Primary,
336 section: crate::xref::Section,
337 expected_end: Option<u64>,
338 work: &impl Work,
339) -> Result<Advanced, ReadFailure<S::Error>> {
340 let Records {
341 entries,
342 sections,
343 lengths,
344 } = records;
345 let Primary {
346 offset,
347 upper,
348 start_entries,
349 section_index: primary,
350 revision,
351 location,
352 } = context;
353 let mut final_section_end = section.end;
354 let forward = section.previous.filter(|&previous| previous > offset);
355 if let Some(main) = forward {
356 (arena, final_section_end) = self::forward(
357 cursor,
358 arena,
359 Records {
360 entries,
361 sections,
362 lengths,
363 },
364 Forward {
365 main,
366 upper,
367 start_entries,
368 section_end: section.end,
369 primary,
370 revision,
371 location,
372 },
373 work,
374 )
375 .await?;
376 }
377 let historical = revision_tail(
378 cursor,
379 &arena,
380 §ions[primary..],
381 RevisionTail {
382 offset,
383 final_section_end,
384 final_end: expected_end,
385 trailer: section.trailer,
386 },
387 work,
388 )
389 .await?;
390 Ok(Advanced {
391 arena,
392 historical,
393 forward,
394 })
395}
396
397async fn primary_section<S: RangeSource, W: Work>(
398 cursor: &mut Cursor<'_, S>,
399 arena: Arena<Memory>,
400 records: Records<'_, W>,
401 offset: u64,
402 revision: usize,
403) -> Result<(Arena<Memory>, crate::xref::Section), ReadFailure<S::Error>> {
404 let table = cursor.looking(b"xref").await?;
406 let (arena, section) = if table {
407 crate::xref::table(cursor, arena, records.entries, revision, 0).await?
408 } else {
409 crate::xref::stream(cursor, arena, records.entries, revision, 0).await?
410 };
411 save_section(
412 records.sections,
413 records.lengths,
414 &arena,
415 section,
416 (offset, table),
417 cursor.location(offset, 0),
418 )?;
419 Ok((arena, section))
420}
421
422fn chronological<W: Work>(
423 history: &mut [Historical],
424 entries: &mut OwnedVec<Entry, W>,
425 location: Location,
426 work: &impl Work,
427) -> crate::Result<()> {
428 let count = history.len();
431 admit(work, location, count as u64)?;
432 history.reverse();
433 for entry in &mut **entries {
434 admit(work, entry.location, 1)?;
435 entry.revision = count - 1 - entry.revision;
436 if let Source::Decoded {
437 stream,
438 revision,
439 stage,
440 } = entry.location.source()
441 {
442 let span = entry.location.span();
443 entry.location = Location::new(
444 Source::Decoded {
445 stream,
446 revision: count - 1 - revision,
447 stage,
448 },
449 span.start,
450 span.length,
451 )
452 .expect("unchanged validated decoded extent");
453 }
454 }
455 entries
456 .try_sort_unstable_by(|left, right| {
457 admit(work, right.location, 1)?;
458 Ok::<_, Error>(
459 left.number
460 .cmp(&right.number)
461 .then_with(|| left.revision.cmp(&right.revision))
462 .then_with(|| left.priority.cmp(&right.priority)),
463 )
464 })
465 .map_err(|error| match error {
466 SortError::Resource(cause) => Error {
467 kind: ErrorKind::Resource(cause),
468 location,
469 },
470 SortError::Comparison(error) => error,
471 })?;
472 for pair in entries.windows(2) {
475 admit(work, pair[1].location, 1)?;
476 if pair[0].number == pair[1].number
477 && pair[0].revision == pair[1].revision
478 && pair[0].priority == pair[1].priority
479 {
480 return Err(Error {
481 kind: ErrorKind::Ambiguous(Syntax::CrossReference),
482 location: pair[1].location,
483 });
484 }
485 }
486 Ok(())
487}
488
489impl<'s, S: RangeSource> Document<'s, S> {
490 pub async fn read<W: Work>(
497 source: &'s S,
498 session: &'s Session,
499 operation: &W,
500 ) -> Result<Self, ReadFailure<S::Error>> {
501 let reader = RangeReader::new(source, session, operation)?;
502 let stamp = reader.stamp();
503 let work = reader.execution();
504 let mut cursor = Cursor { reader, at: 0 };
505 header(&mut cursor).await?;
506 let (mut offset, final_end) = terminal(&mut cursor).await?;
507 let mut upper = stamp.length;
508 let mut arena = Arena::new(&work).into_owned();
509 let mut history = work.vector();
510 let mut entries = work.vector();
511 let mut lengths = work.vector();
512 let mut sections = work.vector();
513 let mut records = Records {
514 entries: &mut entries,
515 sections: &mut sections,
516 lengths: &mut lengths,
517 };
518 loop {
519 let location = cursor.location(offset, 0);
520 session.revision(location)?;
521 if offset >= upper {
522 return Err(cursor.failure(Syntax::Cycle, offset));
523 }
524 cursor.at = offset;
525 let start_entries = records.entries.len();
526 let primary = records.sections.len();
527 let (next, section) = primary_section(
528 &mut cursor,
529 arena,
530 records.reborrow(),
531 offset,
532 history.len(),
533 )
534 .await?;
535 arena = next;
536 if let Some(hybrid) = section.hybrid {
537 arena = supplement(
538 &mut cursor,
539 arena,
540 records.reborrow(),
541 Supplement {
542 hybrid,
543 upper,
544 revision: history.len(),
545 location,
546 },
547 )
548 .await?;
549 }
550 let Advanced {
551 arena: next,
552 historical,
553 forward,
554 } = advance_revision(
555 &mut cursor,
556 arena,
557 records.reborrow(),
558 Primary {
559 offset,
560 upper,
561 start_entries,
562 section_index: primary,
563 revision: history.len(),
564 location,
565 },
566 section,
567 if history.is_empty() {
568 Some(final_end)
569 } else {
570 None
571 },
572 &work,
573 )
574 .await?;
575 arena = next;
576 history.try_push(historical).map_err(|cause| Error {
577 kind: ErrorKind::Resource(cause),
578 location,
579 })?;
580 let Some(previous) = section.previous.filter(|_| forward.is_none()) else {
581 break;
582 };
583 upper = offset;
584 offset = previous;
585 }
586 chronological(&mut history, &mut entries, cursor.location(0, 0), &work)?;
587 cursor.reader.check(cursor.location(0, stamp.length))?;
588 let document = Self {
589 source,
590 session,
591 stamp,
592 arena,
593 history: history.into_owned(),
594 entries: entries.into_owned(),
595 sections: sections.into_owned(),
596 };
597 document.validate_lengths(&lengths, &work).await?;
598 Ok(document)
599 }
600
601 async fn validate_lengths<W: Work>(
602 &self,
603 lengths: &[crate::xref::DeferredLength],
604 work: &W,
605 ) -> Result<(), ReadFailure<S::Error>> {
606 let count = self.history.len();
607 for length in lengths {
608 admit(work, length.location, 1)?;
609 let owner = self
610 .object(
611 count - 1 - length.revision,
612 length.number,
613 length.generation,
614 work,
615 )
616 .await?
617 .ok_or(Error {
618 kind: ErrorKind::Syntax(Syntax::Stream),
619 location: length.location,
620 })?;
621 if owner.stream.is_some()
622 || unsigned(&owner.value.arena, owner.value.root, work)? != length.actual
623 {
624 return Err(Error {
625 kind: ErrorKind::Syntax(Syntax::Stream),
626 location: length.location,
627 }
628 .into());
629 }
630 }
631 Ok(())
632 }
633
634 #[must_use]
636 pub fn stamp(&self) -> Stamp<S::Document, S::Generation> {
637 self.stamp
638 }
639
640 pub(crate) fn check(
641 &self,
642 operation: &impl Work,
643 location: Location,
644 ) -> Result<(), crate::range::InputError<S::Error>> {
645 self.session
646 .query(operation)
647 .charge_work(1)
648 .map_err(|cause| crate::range::InputError::Resource { location, cause })?;
649 if self.source.stamp() != self.stamp {
650 return Err(crate::range::InputError::Changed { location });
651 }
652 Ok(())
653 }
654
655 fn original_reader<'q, W: Work>(
656 &'q self,
657 operation: &'q W,
658 ) -> Result<RangeReader<'q, S>, crate::range::InputError<S::Error>> {
659 let reader = RangeReader::new(self.source, self.session, operation)?;
660 if reader.stamp() != self.stamp {
661 return Err(crate::range::InputError::Changed {
662 location: reader.location(0, 0),
663 });
664 }
665 Ok(reader)
666 }
667
668 pub(crate) async fn string<W: Work>(
669 &self,
670 owner: &Reading<'_, S::Document, S::Generation>,
671 node: NodeId,
672 maximum: usize,
673 operation: &W,
674 ) -> Result<OwnedVec<u8, Memory>, ReadFailure<S::Error>> {
675 self.string_in(owner, owner.decoded.as_deref(), node, maximum, operation)
676 .await
677 }
678
679 pub(crate) async fn string_in<W: Work>(
680 &self,
681 owner: &Reading<'_, S::Document, S::Generation>,
682 decoded: Option<&[u8]>,
683 node: NodeId,
684 maximum: usize,
685 operation: &W,
686 ) -> Result<OwnedVec<u8, Memory>, ReadFailure<S::Error>> {
687 let original = owner.value.arena.nodes[node.0];
688 let crate::arena::Kind::String(form) = original.kind else {
689 return Err(Error {
690 kind: ErrorKind::Syntax(Syntax::String),
691 location: original.location,
692 }
693 .into());
694 };
695 if !core::ptr::eq(owner.value.session, self.session) || owner.value.stamp != self.stamp {
696 return Err(Error {
697 kind: ErrorKind::Foreign,
698 location: original.location,
699 }
700 .into());
701 }
702 self.check(operation, original.location)?;
703 let body = structured_asn1::Span {
704 start: original.location.span().start + 1,
705 length: original.location.span().length - 2,
706 };
707 let output = if let Some(decoded) = decoded {
708 if original.location.source() == Source::File {
709 return Err(Error {
710 kind: ErrorKind::Foreign,
711 location: original.location,
712 }
713 .into());
714 }
715 let slice = crate::range::Slice::new(decoded, self.stamp.snapshot);
716 let mut reader =
717 RangeReader::in_source(&slice, self.session, operation, original.location.source())
718 .map_err(|cause| resolve::convert(ReadFailure::Input(cause)))?;
719 reader
720 .decode_bytes_limit(body, form, maximum)
721 .await
722 .map_err(resolve::convert)?
723 .into_owned()
724 } else {
725 if original.location.source() != Source::File {
726 return Err(Error {
727 kind: ErrorKind::Foreign,
728 location: original.location,
729 }
730 .into());
731 }
732 let mut reader = RangeReader::new(self.source, self.session, operation)?;
733 reader
734 .decode_bytes_limit(body, form, maximum)
735 .await?
736 .into_owned()
737 };
738 self.check(operation, original.location)?;
739 Ok(output)
740 }
741
742 #[must_use]
744 pub fn revisions(&self) -> usize {
745 self.history.len()
746 }
747
748 #[must_use]
751 pub fn revision(&self, ordinal: usize) -> Option<Revision> {
752 self.history.get(ordinal).map(|entry| entry.facts)
753 }
754
755 pub(crate) async fn root<W: Work>(
756 &self,
757 revision: usize,
758 operation: &W,
759 ) -> Result<Reading<'s, S::Document, S::Generation>, ReadFailure<S::Error>> {
760 let work = self.session.query(operation);
761 let Some(historical) = self.history.get(revision) else {
762 return Err(Error {
763 kind: ErrorKind::Foreign,
764 location: Location::new(Source::File, 0, 0).expect("file point"),
765 }
766 .into());
767 };
768 let root = crate::model::required(&self.arena, historical.trailer, b"Root", &work)?;
769 let crate::arena::Kind::Reference { number, generation } = self.arena.nodes[root.0].kind
770 else {
771 return Err(Error {
772 kind: ErrorKind::Syntax(Syntax::Trailer),
773 location: self.arena.nodes[root.0].location,
774 }
775 .into());
776 };
777 self.object(revision, number, generation, &work)
778 .await?
779 .ok_or(
780 Error {
781 kind: ErrorKind::Syntax(Syntax::Trailer),
782 location: self.arena.nodes[root.0].location,
783 }
784 .into(),
785 )
786 }
787
788 pub(crate) async fn select<W: Work>(
789 &self,
790 mut owner: Reading<'s, S::Document, S::Generation>,
791 key: &[u8],
792 operation: &W,
793 ) -> Result<Option<Reading<'s, S::Document, S::Generation>>, ReadFailure<S::Error>> {
794 let work = self.session.query(operation);
795 let Some(node) = get(&owner.value.arena, owner.value.root, key, &work)? else {
796 return Ok(None);
797 };
798 if matches!(
799 owner.value.arena.nodes[node.0].kind,
800 crate::arena::Kind::Reference { .. }
801 ) {
802 match self.resolve_value(&owner, node, &work).await? {
803 Some(resolve::ResolvedValue::Owned(value)) => Ok(Some(value)),
804 None => Ok(None),
805 Some(resolve::ResolvedValue::Borrowed { .. }) => {
806 unreachable!("an indirect selection owns its resolved reading")
807 }
808 }
809 } else {
810 owner.value.root = node;
811 owner.stream = None;
812 Ok(Some(owner))
813 }
814 }
815
816 pub(crate) fn target(
817 &self,
818 revision: usize,
819 number: u64,
820 generation: u16,
821 operation: &impl Work,
822 ) -> crate::Result<Option<Target>> {
823 let location = Location::new(Source::File, 0, 0).expect("file point");
824 if revision >= self.history.len() {
825 return Err(Error {
826 kind: ErrorKind::Foreign,
827 location,
828 });
829 }
830 let work = self.session.query(operation);
831 let mut low = 0;
832 let mut high = self.entries.len();
833 while low < high {
834 let middle = low + (high - low) / 2;
835 let entry = self.entries[middle];
836 admit(&work, entry.location, 1)?;
837 if entry.number < number || (entry.number == number && entry.revision <= revision) {
838 low = middle + 1;
839 } else {
840 high = middle;
841 }
842 }
843 if low == 0 {
844 return Ok(None);
845 }
846 let entry = self.entries[low - 1];
847 admit(&work, entry.location, 1)?;
848 if entry.number != number {
849 return Ok(None);
850 }
851 Ok(match entry.target {
852 Target::File {
853 generation: actual, ..
854 } if generation == actual => Some(entry.target),
855 Target::Compressed { .. } if generation == 0 => Some(entry.target),
856 _ => None,
857 })
858 }
859}
860
861async fn header<S: RangeSource>(cursor: &mut Cursor<'_, S>) -> Result<(), ReadFailure<S::Error>> {
862 if !cursor.looking(b"%PDF-1.").await? {
863 return Err(cursor.failure(Syntax::Header, 0));
864 }
865 cursor.at = 7;
866 if !matches!(cursor.take().await?, Some(b'0'..=b'7'))
867 || !matches!(cursor.take().await?, Some(b'\r' | b'\n'))
868 {
869 return Err(cursor.failure(Syntax::Header, cursor.at));
870 }
871 Ok(())
872}
873
874async fn terminal<S: RangeSource>(
875 cursor: &mut Cursor<'_, S>,
876) -> Result<(u64, u64), ReadFailure<S::Error>> {
877 let mut at = cursor.reader.stamp().length;
880 while at > 0 {
881 at -= 1;
882 if cursor.reader.byte_at(at).await? != Some(b's') {
883 continue;
884 }
885 cursor.at = at;
886 if !cursor.looking(b"startxref").await? {
887 continue;
888 }
889 cursor.keyword(b"startxref").await?;
890 cursor.space().await?;
891 let offset = cursor.natural().await?;
892 let end = finish_footer(cursor).await?;
893 cursor.space().await?;
894 if cursor.peek().await?.is_some() {
895 return Err(cursor.failure(Syntax::StartXref, cursor.at));
896 }
897 return Ok((offset, end));
898 }
899 Err(cursor.failure(Syntax::StartXref, 0))
900}
901
902async fn footer<S: RangeSource>(
903 cursor: &mut Cursor<'_, S>,
904 expected: u64,
905) -> Result<u64, ReadFailure<S::Error>> {
906 cursor.skip().await?;
907 cursor.keyword(b"startxref").await?;
908 cursor.space().await?;
909 let at = cursor.at;
910 if cursor.natural().await? != expected {
911 return Err(cursor.failure(Syntax::StartXref, at));
912 }
913 finish_footer(cursor).await
914}
915
916async fn finish_footer<S: RangeSource>(
917 cursor: &mut Cursor<'_, S>,
918) -> Result<u64, ReadFailure<S::Error>> {
919 cursor.space().await?;
920 if !cursor.looking(b"%%EOF").await? {
921 return Err(cursor.failure(Syntax::StartXref, cursor.at));
922 }
923 cursor.at += 5;
924 match cursor.peek().await? {
925 Some(b'\r') => {
926 cursor.at += 1;
927 if cursor.peek().await? == Some(b'\n') {
928 cursor.at += 1;
929 }
930 }
931 Some(b'\n') => cursor.at += 1,
932 None => {}
933 _ => return Err(cursor.failure(Syntax::StartXref, cursor.at)),
934 }
935 Ok(cursor.at)
936}