1use std::collections::{BTreeMap, BTreeSet, HashMap};
8use std::fmt;
9use std::num::NonZeroUsize;
10use std::ops::Range;
11use std::sync::Arc;
12
13use cadmpeg_core::decode::{u64_from_index, DecodeContext};
14use cadmpeg_core::CodecError;
15use cadmpeg_ir::scalar::FiniteReal;
16
17use self::implementation_level::{DeclaredImplementationLevel, ImplementationLevel};
18
19pub(crate) mod implementation_level;
20
21use crate::lex::{BinaryValue, LexError, Lexer, LiteralAdmission, Token, TokenKind};
22use crate::parse::schema_identifier::{
23 split_schema_identifier, valid_schema_identifier, AdmittedSchemaIdentifier,
24};
25
26mod metadata;
27mod parameters;
28mod recovery;
29pub(crate) mod schema_identifier;
30
31use self::parameters::{ParameterValues, RecordParameters};
32
33#[derive(Debug, Clone, PartialEq)]
35pub(crate) enum Value {
36 Reference(u64),
38 ExternalReference(u64),
40 ConstantEntity(String),
42 ExpressValueConstant(String),
44 Integer(i64),
46 Real(FiniteReal),
48 UninterpretedLiteral,
50 Enumeration(String),
52 String(Vec<u8>),
54 Binary(BinaryValue),
56 Resource(String),
58 Omitted,
60 Derived,
62 List(Vec<Value>),
64 Typed(String, Box<Value>),
66}
67
68fn try_clone_value(value: &Value, budget: &DecodeContext<'_>) -> Result<Value, CodecError> {
69 let _depth = budget.enter_nested("step_value_copy_depth")?;
70 Ok(match value {
71 Value::Reference(id) => Value::Reference(*id),
72 Value::ExternalReference(id) => Value::ExternalReference(*id),
73 Value::ConstantEntity(text) => Value::ConstantEntity(text.to_owned()),
74 Value::ExpressValueConstant(text) => Value::ExpressValueConstant(text.to_owned()),
75 Value::Integer(value) => Value::Integer(*value),
76 Value::Real(value) => Value::Real(*value),
77 Value::UninterpretedLiteral => Value::UninterpretedLiteral,
78 Value::Enumeration(text) => Value::Enumeration(text.to_owned()),
79 Value::String(bytes) => Value::String(bytes.clone()),
80 Value::Binary(binary) => Value::Binary(binary.clone()),
81 Value::Resource(text) => Value::Resource(text.to_owned()),
82 Value::Omitted => Value::Omitted,
83 Value::Derived => Value::Derived,
84 Value::List(values) => {
85 let mut copied = Vec::new();
86 for value in values {
87 copied.push(try_clone_value(value, budget)?);
88 }
89 Value::List(copied)
90 }
91 Value::Typed(name, nested) => {
92 Value::Typed(name.to_owned(), Box::new(try_clone_value(nested, budget)?))
93 }
94 })
95}
96
97fn node_payload(value: &Value) -> u64 {
100 let octets = match value {
101 Value::ConstantEntity(text)
102 | Value::ExpressValueConstant(text)
103 | Value::Enumeration(text)
104 | Value::Resource(text)
105 | Value::Typed(text, _) => text.len(),
106 Value::String(bytes) => bytes.len(),
107 Value::Binary(binary) => binary.data().len(),
108 Value::Reference(_)
109 | Value::ExternalReference(_)
110 | Value::Integer(_)
111 | Value::Real(_)
112 | Value::UninterpretedLiteral
113 | Value::Omitted
114 | Value::Derived
115 | Value::List(_) => 0,
116 };
117 u64_from_index(octets)
118}
119
120const MAX_EXPANDED_PAYLOAD_PER_INPUT_BYTE: u64 = 16;
123const EXPANDED_PAYLOAD_BASE: u64 = 16 * 1024 * 1024;
126
127#[derive(Debug)]
131struct PayloadAllowance {
132 limit: u64,
133 remaining: u64,
134}
135
136impl PayloadAllowance {
137 fn limit_for_input(input_len: usize) -> Option<u64> {
140 MAX_EXPANDED_PAYLOAD_PER_INPUT_BYTE
141 .checked_mul(u64_from_index(input_len))?
142 .checked_add(EXPANDED_PAYLOAD_BASE)
143 }
144
145 fn new(limit: u64) -> Self {
146 Self {
147 limit,
148 remaining: limit,
149 }
150 }
151
152 fn charge(&mut self, octets: u64) -> bool {
155 match self.remaining.checked_sub(octets) {
156 Some(rest) => {
157 self.remaining = rest;
158 true
159 }
160 None => false,
161 }
162 }
163
164 fn bound(&self, expansion: &str) -> String {
165 format!(
166 "{expansion} expansion exceeds {} payload bytes (MAX_EXPANDED_PAYLOAD_PER_INPUT_BYTE)",
167 self.limit
168 )
169 }
170}
171
172#[derive(Debug, Clone, PartialEq)]
174pub(crate) struct PartialRecord {
175 pub(crate) name: String,
177 pub(crate) parameters: RecordParameters,
179}
180
181pub(crate) mod partials {
182 use super::PartialRecord;
183
184 #[derive(Debug, Clone, PartialEq)]
186 pub(crate) struct RecordPartials(Storage);
187
188 #[derive(Debug, Clone, PartialEq)]
189 enum Storage {
190 One(PartialRecord),
191 Many(Vec<PartialRecord>),
192 }
193
194 impl RecordPartials {
195 pub(crate) fn single(first: PartialRecord) -> Self {
197 Self(Storage::One(first))
198 }
199
200 pub(super) fn push(self, next: PartialRecord) -> Self {
201 let records = match self.0 {
202 Storage::One(first) => vec![first, next],
203 Storage::Many(mut records) => {
204 records.push(next);
205 records
206 }
207 };
208 Self(Storage::Many(records))
209 }
210
211 pub(super) fn shrink_to_fit(&mut self) {
212 if let Storage::Many(records) = &mut self.0 {
213 records.shrink_to_fit();
214 }
215 }
216
217 pub(crate) fn first(&self) -> &PartialRecord {
219 &self[0]
220 }
221 }
222
223 impl std::ops::Deref for RecordPartials {
224 type Target = [PartialRecord];
225
226 fn deref(&self) -> &Self::Target {
227 match &self.0 {
228 Storage::One(first) => std::slice::from_ref(first),
229 Storage::Many(records) => records,
230 }
231 }
232 }
233
234 impl std::ops::DerefMut for RecordPartials {
235 fn deref_mut(&mut self) -> &mut Self::Target {
236 match &mut self.0 {
237 Storage::One(first) => std::slice::from_mut(first),
238 Storage::Many(records) => records,
239 }
240 }
241 }
242
243 impl<'a> IntoIterator for &'a RecordPartials {
244 type Item = &'a PartialRecord;
245 type IntoIter = std::slice::Iter<'a, PartialRecord>;
246
247 fn into_iter(self) -> Self::IntoIter {
248 self.iter()
249 }
250 }
251
252 impl<'a> IntoIterator for &'a mut RecordPartials {
253 type Item = &'a mut PartialRecord;
254 type IntoIter = std::slice::IterMut<'a, PartialRecord>;
255
256 fn into_iter(self) -> Self::IntoIter {
257 self.iter_mut()
258 }
259 }
260 #[cfg(test)]
261 mod tests {
262 use super::{PartialRecord, RecordPartials};
263 use crate::parse::parameters::RecordParameters;
264
265 #[test]
266 fn one_partial_is_inline_and_a_second_leaf_is_appended() {
267 let partial = || PartialRecord {
268 name: String::from("ITEM"),
269 parameters: RecordParameters::default(),
270 };
271 let parts = RecordPartials::single(partial());
272 assert_eq!(parts.len(), 1);
273 assert_eq!(parts.first().name, "ITEM");
274 let parts = RecordPartials::single(partial()).push(partial());
275 assert_eq!(parts.len(), 2);
276 }
277 }
278}
279
280#[derive(Debug, Clone, PartialEq)]
282pub(crate) struct RawRecord {
283 pub(crate) partials: partials::RecordPartials,
285 pub(crate) span: Range<usize>,
287}
288
289impl RawRecord {
290 fn values_mut(&mut self) -> impl Iterator<Item = &mut Value> {
292 (&mut self.partials)
293 .into_iter()
294 .flat_map(|partial| &mut partial.parameters)
295 }
296}
297
298#[derive(Debug, Clone, PartialEq)]
300pub(crate) struct HeaderRecord {
301 pub(crate) name: String,
303 pub(crate) parameters: Vec<Value>,
305 pub(crate) offset: usize,
307 pub(crate) end: usize,
309}
310
311#[derive(Debug, Clone, PartialEq)]
313pub(crate) struct DataSection {
314 pub(crate) parameters: Vec<Value>,
316 pub(crate) span: Range<usize>,
318}
319
320#[derive(Debug, Clone, PartialEq)]
322pub(crate) struct AnchorEntry {
323 pub(crate) name: String,
325 pub(crate) value: Value,
327 tags: Vec<AnchorTag>,
329}
330
331impl AnchorEntry {
332 fn values(&self) -> impl Iterator<Item = &Value> {
334 std::iter::once(&self.value).chain(self.tags.iter().map(|tag| &tag.value))
335 }
336
337 fn values_mut(&mut self) -> impl Iterator<Item = &mut Value> {
338 std::iter::once(&mut self.value).chain(self.tags.iter_mut().map(|tag| &mut tag.value))
339 }
340}
341
342#[derive(Debug, Clone, PartialEq)]
344struct AnchorTag {
345 name: String,
347 value: Value,
349}
350
351#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
353pub(crate) enum ReferenceName {
354 Entity(u64),
355 Value(u64),
356}
357
358impl std::fmt::Display for ReferenceName {
359 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360 match self {
361 Self::Entity(id) => write!(formatter, "#{id}"),
362 Self::Value(id) => write!(formatter, "@{id}"),
363 }
364 }
365}
366
367#[derive(Debug, Clone, PartialEq, Eq)]
369pub(crate) struct ReferenceEntry {
370 pub(crate) name: ReferenceName,
372 pub(crate) uri: String,
374}
375
376#[derive(Debug, Clone, PartialEq)]
378pub(crate) struct Exchange {
379 header: Vec<HeaderRecord>,
381 anchors: Vec<AnchorEntry>,
383 references: Vec<ReferenceEntry>,
385 data: Vec<DataSection>,
387 signatures: Vec<Range<usize>>,
389 records: BTreeMap<u64, RawRecord>,
391 omitted_spans: Vec<Range<usize>>,
393 schema_identifiers: Vec<AdmittedSchemaIdentifier>,
394 implementation_level: DeclaredImplementationLevel,
395 entity_ids: EntityIndex,
396}
397
398#[derive(Debug, Clone, Default)]
399struct EntityIndex(Arc<HashMap<String, Vec<u64>>>);
400
401impl PartialEq for EntityIndex {
402 fn eq(&self, _other: &Self) -> bool {
403 true
404 }
405}
406
407impl EntityIndex {
408 fn build(records: &BTreeMap<u64, RawRecord>) -> EntityIndex {
409 let mut index = HashMap::<String, Vec<u64>>::new();
410 for (&id, record) in records {
411 for partial in &record.partials {
412 if let Some(ids) = index.get_mut(partial.name.as_str()) {
413 ids.push(id);
414 } else {
415 let name = partial.name.clone();
416 let ids = vec![id];
417 index.insert(name, ids);
418 }
419 }
420 }
421 Self(Arc::new(index))
422 }
423}
424
425impl Exchange {
426 pub(crate) fn header(&self) -> &[HeaderRecord] {
428 &self.header
429 }
430
431 pub(crate) fn anchors(&self) -> &[AnchorEntry] {
433 &self.anchors
434 }
435
436 pub(crate) fn references(&self) -> &[ReferenceEntry] {
438 &self.references
439 }
440
441 pub(crate) fn data(&self) -> &[DataSection] {
443 &self.data
444 }
445
446 pub(crate) fn signatures(&self) -> &[Range<usize>] {
448 &self.signatures
449 }
450
451 pub(crate) fn records(&self) -> &BTreeMap<u64, RawRecord> {
453 &self.records
454 }
455
456 pub(crate) fn omitted_spans(&self) -> &[Range<usize>] {
457 &self.omitted_spans
458 }
459
460 pub(crate) fn schema_identifiers(&self) -> impl Iterator<Item = &str> {
462 self.schema_identifiers
463 .iter()
464 .map(AdmittedSchemaIdentifier::text)
465 }
466
467 pub(crate) fn joined_schema_identifiers(&self) -> String {
468 self.schema_identifiers().collect::<Vec<_>>().join(",")
469 }
470
471 pub(crate) fn primary_schema_object_identifier(&self) -> Option<Vec<u64>> {
473 self.schema_identifiers
474 .first()
475 .and_then(AdmittedSchemaIdentifier::numeric_object_identifier)
476 }
477
478 pub(crate) fn implementation_level(&self) -> &str {
480 self.implementation_level.text()
481 }
482
483 pub(crate) fn decode_string(
484 &self,
485 bytes: &[u8],
486 ) -> Result<String, crate::strings::StringError> {
487 crate::strings::decode(bytes, self.implementation_level.level())
488 }
489
490 pub(crate) fn release_source_graph(&mut self) -> Vec<Range<usize>> {
492 self.header.clear();
493 self.anchors.clear();
494 self.references.clear();
495 self.data.clear();
496 self.records.clear();
497 self.omitted_spans.clear();
498 self.schema_identifiers.clear();
499 self.entity_ids = EntityIndex::default();
500 std::mem::take(&mut self.signatures)
501 }
502
503 fn entity_ids(&self) -> &HashMap<String, Vec<u64>> {
505 &self.entity_ids.0
506 }
507
508 pub(crate) fn has_entity(&self, name: &str) -> bool {
509 self.entity_ids().contains_key(name)
510 }
511
512 pub(crate) fn has_entity_matching(&self, matches: impl Fn(&str) -> bool) -> bool {
513 self.entity_ids().keys().any(|name| matches(name))
514 }
515
516 pub(crate) fn matching_entity_ids<'a>(
517 &'a self,
518 matches: impl Fn(&str) -> bool + 'a,
519 ) -> impl Iterator<Item = u64> + 'a {
520 self.records.iter().filter_map(move |(&id, record)| {
521 record
522 .partials
523 .iter()
524 .any(|partial| matches(&partial.name))
525 .then_some(id)
526 })
527 }
528
529 pub(crate) fn entities(&self, name: &str) -> impl Iterator<Item = (u64, &RawRecord)> {
530 self.entity_ids()
531 .get(name)
532 .into_iter()
533 .flatten()
534 .map(|id| (*id, &self.records[id]))
535 }
536
537 pub(crate) fn entities_any<'a>(
538 &'a self,
539 names: &'a [&str],
540 ) -> impl Iterator<Item = (u64, &'a RawRecord)> + 'a {
541 self.records.iter().filter_map(move |(&id, record)| {
542 record
543 .partials
544 .iter()
545 .any(|partial| names.contains(&partial.name.as_str()))
546 .then_some((id, record))
547 })
548 }
549}
550
551#[derive(Debug, thiserror::Error)]
553pub(crate) enum ParseError {
554 #[error(transparent)]
556 Lex(#[from] LexError),
557 #[error(transparent)]
559 Resource(#[from] CodecError),
560 #[error("{message} at byte {offset}")]
562 Syntax {
563 offset: usize,
565 message: String,
567 },
568}
569
570#[derive(Debug, Clone, Copy, PartialEq, Eq)]
572pub(crate) enum ParseDiagnosticKind {
573 RecordOmitted,
575 PreambleOmitted,
577 DraftExchangeGrammar,
579 EnvelopeIncomplete,
581 HeaderMetadataNoncanonical,
583 PresentationMetadataUnusable,
585 ComplexPartialsNotAlphabetical,
587 OmittedEntityName,
589 EntityNameUnreadable,
591 SchemaObjectIdentifierOutOfRange,
594 ImplementationLevelUnverified,
597 ExpansionBounded,
600}
601
602#[derive(Debug, Clone, PartialEq, Eq)]
604pub(crate) struct ParseDiagnostic {
605 pub(crate) offset: usize,
607 pub(crate) kind: ParseDiagnosticKind,
609 pub(crate) message: String,
611}
612
613pub(crate) fn parse(
615 input: &[u8],
616 ctx: &DecodeContext<'_>,
617) -> Result<(Exchange, Vec<ParseDiagnostic>), CodecError> {
618 parse_inner(input, ctx).map_err(ParseError::into_codec_error)
619}
620
621pub(crate) fn parse_inner(
622 input: &[u8],
623 budget: &DecodeContext<'_>,
624) -> Result<(Exchange, Vec<ParseDiagnostic>), ParseError> {
625 let lexer = Lexer::new(input, budget);
626 let mut parser = Parser {
627 current: None,
628 lexer,
629 last_end: 0,
630 diagnostics: Vec::new(),
631 omitted_entity_names: None,
632 defer_lexical_errors: false,
633 budget,
634 };
635 parser.current = parser.lex_next()?;
636 parser.exchange()
637}
638
639impl ParseError {
640 fn into_codec_error(self) -> CodecError {
641 match self {
642 Self::Resource(error) => error,
643 Self::Lex(error) => error.into_codec_error(),
644 error @ Self::Syntax { .. } => CodecError::Malformed(error.to_string()),
645 }
646 }
647}
648
649struct Parser<'input, 'ctx, 'arena> {
650 lexer: Lexer<'input, 'ctx, 'arena>,
651 current: Option<Token>,
652 last_end: usize,
653 diagnostics: Vec<ParseDiagnostic>,
654 omitted_entity_names: Option<(usize, NonZeroUsize)>,
655 defer_lexical_errors: bool,
656 budget: &'ctx DecodeContext<'arena>,
657}
658
659struct PartialNameList<'a>(&'a partials::RecordPartials);
660
661impl fmt::Display for PartialNameList<'_> {
662 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
663 for (index, part) in self.0.iter().enumerate() {
664 if index != 0 {
665 output.write_str(", ")?;
666 }
667 output.write_str(&part.name)?;
668 }
669 Ok(())
670 }
671}
672
673struct SortedPartialNameList<'a>(&'a [&'a str]);
674
675impl fmt::Display for SortedPartialNameList<'_> {
676 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
677 for (index, name) in self.0.iter().enumerate() {
678 if index != 0 {
679 output.write_str(", ")?;
680 }
681 output.write_str(name)?;
682 }
683 Ok(())
684 }
685}
686
687struct HeaderAdmission {
688 implementation_level: DeclaredImplementationLevel,
689 schema_identifiers: Vec<AdmittedSchemaIdentifier>,
690}
691
692fn named_carrier_arities(name: &str) -> &'static [usize] {
699 match name {
700 "CARTESIAN_TRANSFORMATION_OPERATOR" | "CARTESIAN_TRANSFORMATION_OPERATOR_2D" => &[5, 7],
701 "CARTESIAN_TRANSFORMATION_OPERATOR_3D" => &[6, 8],
702 "LOOP" | "REPRESENTATION_ITEM" | "VERTEX" => &[1],
703 "SHELL"
704 | "SHELL_BASED_WIREFRAME_MODEL"
705 | "VERTEX_SHELL"
706 | "WIRE_SHELL"
707 | "CARTESIAN_POINT"
708 | "CLOSED_SHELL"
709 | "CONNECTED_EDGE_SET"
710 | "CONNECTED_FACE_SET"
711 | "DIRECTION"
712 | "DRAUGHTING_CALLOUT"
713 | "EDGE_BASED_WIREFRAME_MODEL"
714 | "EDGE_LOOP"
715 | "FACE_BASED_SURFACE_MODEL"
716 | "FACETED_BREP"
717 | "GEOMETRIC_CURVE_SET"
718 | "GEOMETRIC_SET"
719 | "OPEN_SHELL"
720 | "MANIFOLD_SOLID_BREP"
721 | "PLANE"
722 | "POLYLINE"
723 | "POLY_LOOP"
724 | "REPOSITIONED_TESSELLATED_ITEM"
725 | "SHELL_BASED_SURFACE_MODEL"
726 | "TESSELLATED_GEOMETRIC_SET"
727 | "VERTEX_LOOP"
728 | "VERTEX_POINT" => &[2],
729 "APLL_POINT"
730 | "ADVANCED_BREP_REPRESENTATION"
731 | "CONNECTED_EDGE_SUB_SET"
732 | "ADVANCED_BREP_SHAPE_REPRESENTATION"
733 | "ANNOTATION_OCCURRENCE"
734 | "AXIS1_PLACEMENT"
735 | "AXIS2_PLACEMENT_2D"
736 | "BOUNDARY_CURVE"
737 | "BREP_WITH_VOIDS"
738 | "CIRCLE"
739 | "COMPOSITE_CURVE"
740 | "CONNECTED_FACE_SUB_SET"
741 | "CURVE_REPLICA"
742 | "CYLINDRICAL_SURFACE"
743 | "DEFINITIONAL_REPRESENTATION"
744 | "DRAUGHTING_MODEL"
745 | "EDGE"
746 | "FACE_BOUND"
747 | "FACE_OUTER_BOUND"
748 | "GEOMETRICALLY_BOUNDED_SURFACE_SHAPE_REPRESENTATION"
749 | "GEOMETRICALLY_BOUNDED_WIREFRAME_SHAPE_REPRESENTATION"
750 | "LINE"
751 | "MANIFOLD_SURFACE_SHAPE_REPRESENTATION"
752 | "MAPPED_ITEM"
753 | "MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION"
754 | "MEASURE_REPRESENTATION_ITEM"
755 | "OUTER_BOUNDARY_CURVE"
756 | "PARABOLA"
757 | "PCURVE"
758 | "REPRESENTATION"
759 | "SHAPE_DIMENSION_REPRESENTATION"
760 | "SHAPE_REPRESENTATION"
761 | "SHAPE_REPRESENTATION_WITH_PARAMETERS"
762 | "SPHERICAL_SURFACE"
763 | "SUBFACE"
764 | "SURFACE_OF_LINEAR_EXTRUSION"
765 | "SURFACE_OF_REVOLUTION"
766 | "SURFACE_REPLICA"
767 | "TESSELLATED_CURVE_SET"
768 | "TESSELLATED_SHAPE_REPRESENTATION"
769 | "TESSELLATED_SHELL"
770 | "TESSELLATED_SOLID"
771 | "VECTOR" => &[3],
772 "APLL_POINT_WITH_SURFACE"
773 | "OFFSET_CURVE_2D"
774 | "ADVANCED_FACE"
775 | "ANNOTATION_FILL_AREA_OCCURRENCE"
776 | "ANNOTATION_PLANE"
777 | "AXIS2_PLACEMENT_3D"
778 | "CONICAL_SURFACE"
779 | "CURVE_BOUNDED_SURFACE"
780 | "ELLIPSE"
781 | "FACE_SURFACE"
782 | "HYPERBOLA"
783 | "INTERSECTION_CURVE"
784 | "OFFSET_SURFACE"
785 | "ORIENTED_CLOSED_SHELL"
786 | "ORIENTED_FACE"
787 | "ORIENTED_OPEN_SHELL"
788 | "SEAM_CURVE"
789 | "SUBEDGE"
790 | "SURFACE_CURVE"
791 | "TOROIDAL_SURFACE" => &[4],
792 "TESSELLATED_FACE"
793 | "DEGENERATE_TOROIDAL_SURFACE"
794 | "EDGE_CURVE"
795 | "OFFSET_CURVE_3D"
796 | "ORIENTED_EDGE" => &[5],
797 "BEZIER_CURVE"
798 | "QUASI_UNIFORM_CURVE"
799 | "UNIFORM_CURVE"
800 | "SEAM_EDGE"
801 | "TRIMMED_CURVE" => &[6],
802 "RECTANGULAR_TRIMMED_SURFACE" => &[8],
803 "B_SPLINE_CURVE_WITH_KNOTS" => &[9],
804 "B_SPLINE_SURFACE_WITH_KNOTS" => &[13],
805 _ => &[],
806 }
807}
808
809fn omitted_entity_name(partial: &PartialRecord) -> bool {
810 named_carrier_arities(&partial.name).contains(&(partial.parameters.len() + 1))
811 && !matches!(
812 partial.parameters.first(),
813 Some(Value::String(_) | Value::Omitted)
814 )
815}
816
817impl Parser<'_, '_, '_> {
818 fn exchange(mut self) -> Result<(Exchange, Vec<ParseDiagnostic>), ParseError> {
819 let draft = self.lexer.is_draft();
820 let mut omitted_spans = Vec::new();
821 if let Some(offset) =
822 crate::codec::draft_exchange_offset(self.lexer.input()).filter(|&offset| offset != 0)
823 {
824 self.diagnostic(
825 0,
826 ParseDiagnosticKind::PreambleOmitted,
827 format_args!(
828 "bounded prefix before the draft exchange omitted; exact source retained"
829 ),
830 );
831 omitted_spans.push(0..offset);
832 }
833 if draft {
834 self.diagnostic(0, ParseDiagnosticKind::DraftExchangeGrammar, format_args!("pre-standard STEP/FILE_IDENTIFICATION exchange; DATA parsed with the Part 21 instance grammar, with draft @ assignments and !* comments"));
835 }
836 self.name(if draft { "STEP" } else { "ISO-10303-21" })?;
837 self.punct(&TokenKind::Semicolon)?;
838 self.name("HEADER")?;
839 self.defer_lexical_errors = true;
840 self.lexer.set_literal_admission(LiteralAdmission::Metadata);
841 self.punct(&TokenKind::Semicolon)?;
842 let mut header = Vec::new();
843 while !self.peek_name("ENDSEC") && self.current.is_some() {
844 if let Some(record) = self.recover_header_record(&mut omitted_spans)? {
845 header.push(record);
846 }
847 }
848 self.defer_lexical_errors = false;
849 self.lexer.set_literal_admission(LiteralAdmission::Required);
850 self.name("ENDSEC")?;
851 self.punct(&TokenKind::Semicolon)?;
852 let admission = if draft && !header.iter().any(|record| record.name == "FILE_SCHEMA") {
853 Ok((
854 HeaderAdmission {
855 implementation_level: DeclaredImplementationLevel::new(String::new()),
856 schema_identifiers: Vec::new(),
857 },
858 Vec::new(),
859 ))
860 } else {
861 validate_header(&header)
862 };
863 let (header_admission, header_diagnostics) = match admission {
864 Ok(admitted) => admitted,
865 Err(ValidationError::Invalid(message)) => return self.err(message),
866 Err(ValidationError::Resource(error)) => return Err(ParseError::Resource(error)),
867 };
868 let implementation_level = header_admission.implementation_level.level();
869
870 self.diagnostics.extend(header_diagnostics);
871 let schema_names_for_matching =
872 schema_names_for_matching(&header_admission.schema_identifiers);
873 let header_data_references = match validate_header_sections(
874 implementation_level,
875 &header,
876 &schema_names_for_matching,
877 ) {
878 Ok(references) => references,
879 Err(ValidationError::Invalid(message)) => {
880 push_header_metadata_diagnostic(
881 &mut self.diagnostics,
882 header.first().map_or(0, |record| record.offset),
883 format_args!("{message}"),
884 );
885 Vec::new()
886 }
887 Err(ValidationError::Resource(error)) => return Err(ParseError::Resource(error)),
888 };
889 let mut anchors = Vec::new();
890 let mut anchor_spans = Vec::new();
891 if let Some(level) = implementation_level.edition3_sections_forbidden_by() {
892 if self.peek_name("ANCHOR") || self.peek_name("REFERENCE") {
893 self.diagnostic(self.current_offset(), ParseDiagnosticKind::ImplementationLevelUnverified, format_args!("{level} forbids ANCHOR and REFERENCE sections; readable sections parsed with the 4;3 section grammar"));
894 }
895 }
896 if self.peek_name("ANCHOR") {
897 self.lexer.set_allow_print_controls(false);
898 self.next_kind()?;
899 self.punct(&TokenKind::Semicolon)?;
900 while !self.peek_name("ENDSEC") {
901 let start = self.current_offset();
902 let TokenKind::Resource(name) = self.next_kind()? else {
903 return self.err("expected anchor name");
904 };
905 if !valid_anchor_name(&name) {
906 return self.err("anchor name must contain a non-digit character");
907 }
908 self.punct(&TokenKind::Equals)?;
909 let value = self.value()?;
910 if !is_anchor_item(&value) {
911 return self.err("invalid anchor item");
912 }
913 let mut tags = Vec::new();
914 while self.peek(&TokenKind::LBrace) {
915 self.next_kind()?;
916 let TokenKind::TagName(name) = self.next_kind()? else {
917 return self.err("expected anchor tag name");
918 };
919 self.punct(&TokenKind::Colon)?;
920 let value = self.value()?;
921 if !is_anchor_item(&value) {
922 return self.err("invalid anchor tag item");
923 }
924 self.punct(&TokenKind::RBrace)?;
925
926 tags.push(AnchorTag { name, value });
927 }
928 tags.shrink_to_fit();
929
930 self.punct(&TokenKind::Semicolon)?;
931 anchors.push(AnchorEntry { name, value, tags });
932 anchor_spans.push(start..self.previous_end());
933 }
934 self.next_kind()?;
935 self.lexer.set_allow_print_controls(true);
936 self.punct(&TokenKind::Semicolon)?;
937 }
938 let mut reference_entries = Vec::new();
939
940 let mut external_reference_ids = BTreeSet::new();
941 let mut external_value_reference_ids = BTreeSet::new();
942 if self.peek_name("REFERENCE") {
943 self.lexer.set_allow_print_controls(false);
944 self.next_kind()?;
945 self.punct(&TokenKind::Semicolon)?;
946 while !self.peek_name("ENDSEC") {
947 let (name, same_kind, other_kind, id) = match self.next_kind()? {
948 TokenKind::Instance(id) => (
949 ReferenceName::Entity(id),
950 &mut external_reference_ids,
951 &external_value_reference_ids,
952 id,
953 ),
954 TokenKind::ValueInstance(id) => (
955 ReferenceName::Value(id),
956 &mut external_value_reference_ids,
957 &external_reference_ids,
958 id,
959 ),
960 _ => return self.err("expected reference name"),
961 };
962 if same_kind.contains(&id) {
963 return self.err("duplicate reference name");
964 }
965 same_kind.insert(id);
966 if other_kind.contains(&id) {
967 return self.err("duplicate external occurrence integer");
968 }
969 self.punct(&TokenKind::Equals)?;
970 let TokenKind::Resource(uri) = self.next_kind()? else {
971 return self.err("expected reference URI");
972 };
973 self.punct(&TokenKind::Semicolon)?;
974 reference_entries.push(ReferenceEntry { name, uri });
975 }
976 self.next_kind()?;
977 self.lexer.set_allow_print_controls(true);
978 self.punct(&TokenKind::Semicolon)?;
979 }
980 let mut data: Vec<DataSection> = Vec::new();
981 let mut records = BTreeMap::new();
982 let mut ambiguous = BTreeSet::new();
983
984 let mut data_section_names = BTreeSet::new();
985 while self.peek_name("DATA") {
986 self.next_kind()?;
987 if implementation_level == ImplementationLevel::LegacyEdition1 && !data.is_empty() {
988 return self.err("2;1 requires one DATA section");
989 }
990 let parameters = if self.peek(&TokenKind::LParen) {
991 if data
992 .first()
993 .is_some_and(|section| section.parameters.is_empty())
994 {
995 return self.err("multiple DATA sections require section parameters");
996 }
997 if implementation_level == ImplementationLevel::LegacyEdition1 {
998 return self.err("2;1 forbids DATA section parameters");
999 }
1000 let parameters = self.parameter_nesting(Self::parameters_inner)?;
1001 if let Err(message) = valid_data_parameters(
1002 ¶meters,
1003 &schema_names_for_matching,
1004 implementation_level,
1005 &mut data_section_names,
1006 ) {
1007 match message {
1008 ValidationError::Invalid(message) => return self.err(message),
1009 ValidationError::Resource(error) => {
1010 return Err(ParseError::Resource(error))
1011 }
1012 }
1013 }
1014 parameters
1015 } else {
1016 if !data.is_empty() {
1017 return self.err("multiple DATA sections require section parameters");
1018 }
1019 Vec::new()
1020 };
1021 self.defer_lexical_errors = true;
1022 self.punct(&TokenKind::Semicolon)?;
1023 let record_start = self.current_offset();
1024 while !self.peek_name("ENDSEC") && self.current.is_some() {
1025 self.recover_data_record(&mut records, &mut ambiguous, &mut omitted_spans)?;
1026 }
1027 let record_end = self.current_offset();
1028 self.defer_lexical_errors = false;
1029 if self.current.is_some() {
1030 self.name("ENDSEC")?;
1031 if self.current.is_some() {
1032 self.punct(&TokenKind::Semicolon)?;
1033 }
1034 }
1035
1036 data.push(DataSection {
1037 parameters,
1038 span: record_start..record_end,
1039 });
1040 }
1041 if !implementation_level.is_edition3() && data.is_empty() {
1042 return self.err("historical implementation levels require one DATA section");
1043 }
1044 if !header_admission.implementation_level.is_unverified()
1045 && implementation_level.is_edition3()
1046 && data.len() == 1
1047 && data[0].parameters.is_empty()
1048 && schema_names_for_matching.len() != 1
1049 {
1050 return self.err("an unnamed DATA section requires one FILE_SCHEMA identifier");
1051 }
1052 if let Err(message) =
1053 validate_header_data_references(&header_data_references, &data_section_names)
1054 {
1055 push_header_metadata_diagnostic(
1056 &mut self.diagnostics,
1057 header.first().map_or(0, |record| record.offset),
1058 format_args!("{message}"),
1059 );
1060 }
1061 if self.current.is_none() && !data.is_empty() {
1062 self.diagnostic(self.current_offset(), ParseDiagnosticKind::EnvelopeIncomplete, format_args!("readable DATA records precede EOF; closing exchange envelope is incomplete; no terminator bytes were supplied"));
1063 } else {
1064 self.name(if draft { "ENDSTEP" } else { "END-ISO-10303-21" })?;
1065 if self.current.is_some() {
1066 self.punct(&TokenKind::Semicolon)?;
1067 } else {
1068 self.diagnostic(self.current_offset(), ParseDiagnosticKind::EnvelopeIncomplete, format_args!("exchange terminator precedes EOF without its closing semicolon; no terminator bytes were supplied"));
1069 }
1070 }
1071 let mut signatures = Vec::new();
1072 if let Some(level) = implementation_level.edition3_sections_forbidden_by() {
1073 if self.peek_name("SIGNATURE") {
1074 return self.err(&format!("{level} forbids SIGNATURE sections"));
1075 }
1076 }
1077 while self.peek_name("SIGNATURE") {
1078 let start = self.current_offset();
1079 self.next_kind()?;
1080 self.punct(&TokenKind::Semicolon)?;
1081 let payload_start = self.last_end;
1082 let payload_end = self.current_offset();
1083 while !self.peek_name("ENDSEC") {
1084 if self.current.is_none() {
1085 return self.err("unterminated SIGNATURE section");
1086 }
1087 self.next_kind()?;
1088 }
1089 self.next_kind()?;
1090 self.punct(&TokenKind::Semicolon)?;
1091 let span = start..self.previous_end();
1092 let payload = payload_start..payload_end;
1093 crate::signature::decode_payload(self.lexer.input(), &payload, self.budget)?;
1094 signatures.push(span);
1095 }
1096 if self.current.is_some() {
1097 return self.err("tokens after exchange terminator");
1098 }
1099 if records.keys().any(|id| external_reference_ids.contains(id)) {
1100 return self.err("external reference instance collides with a DATA instance");
1101 }
1102 if records
1103 .keys()
1104 .any(|id| external_value_reference_ids.contains(id))
1105 {
1106 return self.err("external value instance collides with a DATA instance");
1107 }
1108 let Some(payload_limit) = PayloadAllowance::limit_for_input(self.lexer.input().len())
1109 else {
1110 return self.err("input length overflows the expansion payload allowance");
1111 };
1112 let mut omissions = ExpansionOmissions::default();
1113 if !anchors.is_empty() {
1114 let mut anchor_bindings = BTreeMap::new();
1115 for anchor in &anchors {
1116 let value = try_clone_value(&anchor.value, self.budget)?;
1117 anchor_bindings.insert(anchor.name.clone(), value);
1118 }
1119 if anchor_bindings.len() != anchors.len() {
1120 return self.err("duplicate anchor name");
1121 }
1122 let mut bounded = BoundedExpansions::default();
1123 let mut resolver = AnchorResolver::new(&anchor_bindings, payload_limit, self.budget);
1124 for anchor in &mut anchors {
1125 if let Some(bound) = resolver
1126 .resolve_item(anchor.values_mut())
1127 .map_err(|error| error.into_parse_error(0))?
1128 {
1129 bounded.anchors.insert(anchor.name.clone(), bound);
1130 }
1131 }
1132 for (&id, record) in &mut records {
1133 let offset = record.span.start;
1134 if let Some(bound) = resolver
1135 .resolve_item(record.values_mut())
1136 .map_err(|error| error.into_parse_error(offset))?
1137 {
1138 bounded.records.insert(id, bound);
1139 }
1140 }
1141 self.omit_bounded_expansions(
1142 bounded,
1143 &mut anchors,
1144 &mut anchor_spans,
1145 &mut records,
1146 &mut omitted_spans,
1147 &mut omissions,
1148 );
1149 }
1150 let class3_restriction =
1153 implementation_level
1154 .class3_occurrence_restriction()
1155 .filter(|_| {
1156 header
1157 .iter()
1158 .any(|record| record.parameters.iter().any(contains_class3_occurrence))
1159 || anchors.iter().any(|anchor| {
1160 contains_class3_occurrence(&anchor.value)
1161 || anchor
1162 .tags
1163 .iter()
1164 .any(|tag| contains_class3_occurrence(&tag.value))
1165 })
1166 || records.values().any(|record| {
1167 record.partials.iter().any(|partial| {
1168 partial.parameters.iter().any(contains_class3_occurrence)
1169 })
1170 })
1171 });
1172 let bounded = resolve_local_references(
1173 &mut anchors,
1174 &mut records,
1175 &reference_entries,
1176 &omissions.anchors,
1177 payload_limit,
1178 self.budget,
1179 )?;
1180 self.omit_bounded_expansions(
1181 bounded,
1182 &mut anchors,
1183 &mut anchor_spans,
1184 &mut records,
1185 &mut omitted_spans,
1186 &mut omissions,
1187 );
1188 for (&id, record) in &mut records {
1189 if record.partials.len() == 1 && omitted_entity_name(&record.partials[0]) {
1190 let parameters = &mut record.partials[0].parameters;
1191
1192 parameters.prepend(Value::String(Vec::new()));
1193 match &mut self.omitted_entity_names {
1194 Some((_, count)) => {
1195 let Some(next) = count.checked_add(1) else {
1196 return Self::err_at(
1197 record.span.start,
1198 "omitted entity name count exceeds the address space",
1199 );
1200 };
1201 *count = next;
1202 }
1203 None => {
1204 self.omitted_entity_names = Some((record.span.start, NonZeroUsize::MIN));
1205 }
1206 }
1207 }
1208 for partial in &record.partials {
1209 if (record.partials.len() == 1 || partial.name == "REPRESENTATION_ITEM")
1210 && named_carrier_arities(&partial.name).contains(&partial.parameters.len())
1211 && partial.parameters.first().is_some_and(|value| {
1212 !matches!(value, Value::String(_) | Value::Omitted | Value::Derived)
1213 })
1214 {
1215 self.diagnostics.push(ParseDiagnostic {
1216 offset: record.span.start,
1217 kind: ParseDiagnosticKind::EntityNameUnreadable,
1218 message: format!("{} #{id} has a non-string name; parameter positions and exact source retained", partial.name),
1219 });
1220 }
1221 }
1222 }
1223
1224 let mut refs = Vec::new();
1225 let mut value_refs = Vec::new();
1226 let mut dependent = BoundedExpansions::default();
1227 for anchor in &anchors {
1228 let mut bounded_reference = None;
1229 for value in anchor.values() {
1230 refs.clear();
1231 value_refs.clear();
1232 references(value, &mut refs, &mut value_refs, self.budget)?;
1233 bounded_reference = refs
1234 .iter()
1235 .copied()
1236 .find(|id| omissions.records.contains(id));
1237 if bounded_reference.is_some() {
1238 break;
1239 }
1240 }
1241 if let Some(id) = bounded_reference {
1242 dependent.anchors.insert(
1243 anchor.name.clone(),
1244 format!("depends on instance #{id}, omitted at its expansion limit"),
1245 );
1246 continue;
1247 }
1248 refs.clear();
1249 value_refs.clear();
1250 references(&anchor.value, &mut refs, &mut value_refs, self.budget)?;
1251 if refs
1252 .iter()
1253 .any(|id| !records.contains_key(id) && !external_reference_ids.contains(id))
1254 {
1255 return self.err("unresolved instance reference in anchor binding");
1256 }
1257 if value_refs
1258 .iter()
1259 .any(|id| !external_value_reference_ids.contains(id))
1260 {
1261 return self.err("unresolved value instance reference in anchor binding");
1262 }
1263 for tag in &anchor.tags {
1264 refs.clear();
1265 value_refs.clear();
1266 references(&tag.value, &mut refs, &mut value_refs, self.budget)?;
1267 if refs
1268 .iter()
1269 .any(|id| !records.contains_key(id) && !external_reference_ids.contains(id))
1270 {
1271 return self.err("unresolved instance reference in anchor tag");
1272 }
1273 if value_refs
1274 .iter()
1275 .any(|id| !external_value_reference_ids.contains(id))
1276 {
1277 return self.err("unresolved value instance reference in anchor tag");
1278 }
1279 }
1280 }
1281 self.omit_bounded_expansions(
1282 dependent,
1283 &mut anchors,
1284 &mut anchor_spans,
1285 &mut records,
1286 &mut omitted_spans,
1287 &mut omissions,
1288 );
1289 self.omit_unresolved_records(
1290 &mut records,
1291 &external_reference_ids,
1292 &external_value_reference_ids,
1293 &mut omitted_spans,
1294 )?;
1295 if let Some(message) = class3_restriction {
1296 return self.err(message);
1297 }
1298 let has_resource_value = records.values().any(|record| {
1299 record.partials.iter().any(|partial| {
1300 partial.parameters.iter().enumerate().any(|(index, value)| {
1301 let name_slot = index == 0
1302 && (record.partials.len() == 1 || partial.name == "REPRESENTATION_ITEM")
1303 && named_carrier_arities(&partial.name).contains(&partial.parameters.len());
1304 !name_slot && contains_resource_value(value)
1305 })
1306 })
1307 });
1308 if has_resource_value {
1309 return self.err("resource values are only valid in edition-3 anchor items");
1310 }
1311 if let Some((offset, count)) = self.omitted_entity_names {
1312 self.diagnostics.push(ParseDiagnostic {
1313 offset,
1314 kind: ParseDiagnosticKind::OmittedEntityName,
1315 message: format!(
1316 "recovered {count} simple named carrier instance(s) with an omitted leading name attribute by inserting an empty name"
1317 ),
1318 });
1319 }
1320 header.shrink_to_fit();
1321 anchors.shrink_to_fit();
1322 reference_entries.shrink_to_fit();
1323 data.shrink_to_fit();
1324 signatures.shrink_to_fit();
1325 let entity_ids = EntityIndex::build(&records);
1326 Ok((
1327 Exchange {
1328 header,
1329 anchors,
1330 references: reference_entries,
1331 data,
1332 signatures,
1333 records,
1334 omitted_spans,
1335 schema_identifiers: header_admission.schema_identifiers,
1336 implementation_level: header_admission.implementation_level,
1337 entity_ids,
1338 },
1339 self.diagnostics,
1340 ))
1341 }
1342
1343 fn record(&mut self) -> Result<(u64, RawRecord), ParseError> {
1344 let start = self.current_offset();
1345 let diagnostic_start = self.diagnostics.len();
1346 let id = match self.next_kind()? {
1347 TokenKind::Instance(id) => id,
1348 TokenKind::ValueInstance(id) if self.lexer.is_draft() => id,
1349 _ => return self.err("expected instance name"),
1350 };
1351 self.punct(&TokenKind::Equals)?;
1352 self.budget.charge_entities(1, "step_parse_record")?;
1353 let mut partials = if self.peek(&TokenKind::LParen) {
1354 self.next_kind()?;
1355 let first = self.partial(false)?;
1356 let mut parts = partials::RecordPartials::single(first);
1357 while !self.peek(&TokenKind::RParen) {
1358 let partial = self.partial(false)?;
1359 parts = parts.push(partial);
1360 }
1361 self.next_kind()?;
1362
1363 let mut canonical_names = Vec::new();
1364 for part in &parts {
1365 canonical_names.push(part.name.as_str());
1366 }
1367 canonical_names.sort_unstable_by(Ord::cmp);
1368 if canonical_names
1369 .windows(2)
1370 .any(|window| window[0] == window[1])
1371 {
1372 return Self::err_at(start, "duplicate complex partial name");
1373 }
1374 if !parts
1375 .windows(2)
1376 .all(|window| window[0].name < window[1].name)
1377 {
1378 let message = format!(
1379 "complex partial records are not alphabetical: observed ({}), expected ({})",
1380 PartialNameList(&parts),
1381 SortedPartialNameList(&canonical_names),
1382 );
1383 self.diagnostics.push(ParseDiagnostic {
1384 offset: start,
1385 kind: ParseDiagnosticKind::ComplexPartialsNotAlphabetical,
1386 message,
1387 });
1388 }
1389 parts
1390 } else {
1391 let first = self.partial(true)?;
1392 partials::RecordPartials::single(first)
1393 };
1394 for diagnostic in &mut self.diagnostics[diagnostic_start..] {
1395 if diagnostic.kind == ParseDiagnosticKind::PresentationMetadataUnusable {
1396 diagnostic.offset = start;
1397 }
1398 }
1399 partials.shrink_to_fit();
1400 self.punct(&TokenKind::Semicolon)?;
1401 Ok((
1402 id,
1403 RawRecord {
1404 partials,
1405 span: start..self.previous_end(),
1406 },
1407 ))
1408 }
1409
1410 fn partial(&mut self, simple: bool) -> Result<PartialRecord, ParseError> {
1411 let offset = self.current_offset();
1412 let name = self.take_name()?;
1413 let presentation = metadata::presentation_record(&name);
1414 if presentation {
1415 self.lexer.set_literal_admission(LiteralAdmission::Metadata);
1416 }
1417 let first_is_name =
1418 (simple || name == "REPRESENTATION_ITEM") && !named_carrier_arities(&name).is_empty();
1419 let parameters = self.parameter_nesting(|parser| {
1420 parser.parameters_with_name::<RecordParameters>(first_is_name && !presentation)
1421 })?;
1422 if first_is_name
1423 && matches!(parameters.first(), Some(Value::UninterpretedLiteral))
1424 && !named_carrier_arities(&name).contains(¶meters.len())
1425 {
1426 return self.err("unreadable literal in a required DATA attribute");
1427 }
1428 self.lexer.set_literal_admission(LiteralAdmission::Required);
1429 if presentation && metadata::unreadable_literal(¶meters, self.budget)? {
1430 self.diagnostics.push(ParseDiagnostic {
1431 offset, kind: ParseDiagnosticKind::PresentationMetadataUnusable,
1432 message: format!("{name} contains an unusable bounded presentation literal; exact record retained"),
1433 });
1434 }
1435 Ok(PartialRecord { name, parameters })
1436 }
1437
1438 fn parameter_nesting<T>(
1439 &mut self,
1440 parse: impl FnOnce(&mut Self) -> Result<T, ParseError>,
1441 ) -> Result<T, ParseError> {
1442 let budget = self.budget;
1443 let _nested = budget.enter_nested("step_parse_parameter_nesting")?;
1444 parse(self)
1445 }
1446
1447 fn parameters_inner(&mut self) -> Result<Vec<Value>, ParseError> {
1448 self.parameters_with_name(false)
1449 }
1450
1451 fn parameters_with_name<T: ParameterValues>(
1452 &mut self,
1453 first_is_name: bool,
1454 ) -> Result<T, ParseError> {
1455 if first_is_name {
1456 self.lexer.set_literal_admission(LiteralAdmission::Metadata);
1457 }
1458 self.punct(&TokenKind::LParen)?;
1459 let mut values = T::default();
1460 if self.peek(&TokenKind::RParen) {
1461 if first_is_name {
1462 self.lexer.set_literal_admission(LiteralAdmission::Required);
1463 }
1464 self.next_kind()?;
1465 return Ok(values);
1466 }
1467 loop {
1468 if first_is_name
1469 && values.is_empty()
1470 && matches!(
1471 self.current.as_ref().map(|token| &token.kind),
1472 Some(TokenKind::LParen | TokenKind::Name(_) | TokenKind::UserName(_))
1473 )
1474 {
1475 self.lexer.set_literal_admission(LiteralAdmission::Required);
1478 }
1479 let value = self.value()?;
1480 if first_is_name {
1481 self.lexer.set_literal_admission(LiteralAdmission::Required);
1482 }
1483 values.push_value(value);
1484 if self.peek(&TokenKind::Comma) {
1485 self.next_kind()?;
1486 } else {
1487 break;
1488 }
1489 }
1490 self.punct(&TokenKind::RParen)?;
1491 Ok(values)
1492 }
1493
1494 fn value(&mut self) -> Result<Value, ParseError> {
1495 let value = if self.peek(&TokenKind::LParen) {
1496 Value::List(self.parameter_nesting(Self::parameters_inner)?)
1497 } else {
1498 match self.next_kind()? {
1499 TokenKind::Instance(v) => Value::Reference(v),
1500 TokenKind::ValueInstance(v) => Value::ExternalReference(v),
1501 TokenKind::ConstantEntity(name) => Value::ConstantEntity(name),
1502 TokenKind::ConstantValue(name) => Value::ExpressValueConstant(name),
1503 TokenKind::Integer(v) => Value::Integer(v),
1504 TokenKind::Real(v) => Value::Real(v),
1505 TokenKind::UninterpretedLiteral => Value::UninterpretedLiteral,
1506 TokenKind::Enumeration(value) => Value::Enumeration(value),
1507 TokenKind::String(value) => Value::String(value),
1508 TokenKind::Binary(value) => Value::Binary(value),
1509 TokenKind::Resource(value) => Value::Resource(value),
1510 TokenKind::Omitted => Value::Omitted,
1511 TokenKind::Derived => Value::Derived,
1512 TokenKind::Name(name) => self.typed_parameter(name)?,
1513 TokenKind::UserName(name) => self.typed_parameter(format!("!{name}"))?,
1514 _ => return self.err("expected parameter value"),
1515 }
1516 };
1517 Ok(value)
1518 }
1519
1520 fn typed_parameter(&mut self, name: String) -> Result<Value, ParseError> {
1521 self.parameter_nesting(|parser| {
1522 let metadata = parser.lexer.allows_uninterpreted_literals();
1523 parser.punct(&TokenKind::LParen)?;
1524 if parser.peek(&TokenKind::RParen) {
1525 if metadata {
1526 parser.punct(&TokenKind::RParen)?;
1527 return Ok(Value::UninterpretedLiteral);
1528 }
1529 return parser.err("typed parameter requires one value");
1530 }
1531 let value = parser.value()?;
1532 if parser.peek(&TokenKind::Comma) {
1533 if !metadata {
1534 return parser.err("typed parameter requires one value");
1535 }
1536 while parser.peek(&TokenKind::Comma) {
1537 parser.next_kind()?;
1538 let _ = parser.value()?;
1540 }
1541 parser.punct(&TokenKind::RParen)?;
1542 return Ok(Value::UninterpretedLiteral);
1543 }
1544 parser.punct(&TokenKind::RParen)?;
1545
1546 Ok(Value::Typed(name, Box::new(value)))
1547 })
1548 }
1549
1550 fn take_name(&mut self) -> Result<String, ParseError> {
1551 match self.next_kind()? {
1552 TokenKind::Name(name) => Ok(name),
1553 TokenKind::UserName(name) => Ok(format!("!{name}")),
1554 _ => self.err("expected name"),
1555 }
1556 }
1557 fn name(&mut self, expected: &str) -> Result<(), ParseError> {
1558 let actual = self.take_name()?;
1559 if actual == expected {
1560 Ok(())
1561 } else {
1562 let message = format!("expected {expected}, found {actual}");
1563 Err(ParseError::Syntax {
1564 offset: self.current_offset(),
1565 message,
1566 })
1567 }
1568 }
1569 fn punct(&mut self, expected: &TokenKind) -> Result<(), ParseError> {
1570 let actual = self.next_kind()?;
1571 if std::mem::discriminant(&actual) == std::mem::discriminant(expected) {
1572 Ok(())
1573 } else {
1574 self.err("unexpected token")
1575 }
1576 }
1577 fn peek(&self, expected: &TokenKind) -> bool {
1578 self.current.as_ref().is_some_and(|token| {
1579 std::mem::discriminant(&token.kind) == std::mem::discriminant(expected)
1580 })
1581 }
1582 fn peek_name(&self, expected: &str) -> bool {
1583 matches!(self.current.as_ref().map(|token| &token.kind), Some(TokenKind::Name(name)) if name == expected)
1584 }
1585 fn next_kind(&mut self) -> Result<TokenKind, ParseError> {
1586 let Some(token) = self.current.take() else {
1587 return self.err("unexpected end of input");
1588 };
1589 if let TokenKind::InvalidSource(message) = token.kind {
1590 return Err(ParseError::Syntax {
1591 offset: token.span.start,
1592 message,
1593 });
1594 }
1595 self.last_end = token.span.end;
1596 self.current = self.lex_next()?;
1597 Ok(token.kind)
1598 }
1599 fn lex_next(&mut self) -> Result<Option<Token>, ParseError> {
1600 let token = match self.lexer.next_token() {
1601 Ok(token) => token,
1602 Err(error) if self.defer_lexical_errors => {
1603 let offset = error.offset();
1604 let message = error.to_string();
1605 if let Some(resource) = error.into_resource_error() {
1606 return Err(ParseError::Resource(resource));
1607 }
1608 Some(Token {
1609 kind: TokenKind::InvalidSource(message),
1610 span: offset..offset,
1611 })
1612 }
1613 Err(error) => return Err(ParseError::Lex(error)),
1614 };
1615 Ok(token)
1616 }
1617
1618 fn current_offset(&self) -> usize {
1619 self.current
1620 .as_ref()
1621 .map_or(self.last_end, |token| token.span.start)
1622 }
1623 fn previous_end(&self) -> usize {
1624 self.last_end
1625 }
1626 fn err<T>(&self, message: &str) -> Result<T, ParseError> {
1627 Self::err_at(self.current_offset(), message)
1628 }
1629 fn err_at<T>(offset: usize, message: &str) -> Result<T, ParseError> {
1630 Err(ParseError::Syntax {
1631 offset,
1632 message: message.into(),
1633 })
1634 }
1635}
1636
1637enum ValidationError {
1640 Invalid(&'static str),
1641 Resource(CodecError),
1642}
1643
1644impl From<CodecError> for ValidationError {
1645 fn from(error: CodecError) -> Self {
1646 Self::Resource(error)
1647 }
1648}
1649
1650impl ValidationError {
1651 fn with_message(self, message: &'static str) -> Self {
1652 match self {
1653 Self::Invalid(_) => Self::Invalid(message),
1654 Self::Resource(error) => Self::Resource(error),
1655 }
1656 }
1657}
1658
1659fn invalid<T>(message: &'static str) -> Result<T, ValidationError> {
1660 Err(ValidationError::Invalid(message))
1661}
1662
1663fn push_header_metadata_diagnostic(
1664 diagnostics: &mut Vec<ParseDiagnostic>,
1665 offset: usize,
1666 message: std::fmt::Arguments<'_>,
1667) {
1668 diagnostics.push(ParseDiagnostic {
1669 offset,
1670 kind: ParseDiagnosticKind::HeaderMetadataNoncanonical,
1671 message: message.to_string(),
1672 });
1673}
1674
1675fn validate_header(
1677 header: &[HeaderRecord],
1678) -> Result<(HeaderAdmission, Vec<ParseDiagnostic>), ValidationError> {
1679 const REQUIRED: [&str; 3] = ["FILE_DESCRIPTION", "FILE_NAME", "FILE_SCHEMA"];
1680 let mut diagnostics = Vec::new();
1681 let offset = header.first().map_or(0, |record| record.offset);
1682 let mut schemas = header.iter().filter(|record| record.name == "FILE_SCHEMA");
1683 let Some(schema_record) = schemas.next() else {
1684 return invalid("HEADER has no FILE_SCHEMA");
1685 };
1686 if schemas.next().is_some() {
1687 return invalid("HEADER contains duplicate FILE_SCHEMA");
1688 }
1689 if header.len() < REQUIRED.len()
1690 || header
1691 .iter()
1692 .zip(REQUIRED)
1693 .any(|(record, expected)| record.name != expected)
1694 {
1695 push_header_metadata_diagnostic(&mut diagnostics, offset, format_args!("HEADER does not begin with FILE_DESCRIPTION, FILE_NAME, and FILE_SCHEMA; records read by name"));
1696 }
1697 let mut descriptions = header
1698 .iter()
1699 .filter(|record| record.name == "FILE_DESCRIPTION");
1700 let description = descriptions.next();
1701 let text = match (description, descriptions.next()) {
1702 (Some(record), None) => match record.parameters.get(1) {
1703 Some(Value::String(bytes)) => {
1704 decoded_bytes(bytes, ImplementationLevel::LegacyEdition1).unwrap_or_default()
1705 }
1706 _ => String::new(),
1707 },
1708 _ => String::new(),
1709 };
1710 let declaration = DeclaredImplementationLevel::new(text);
1711 if declaration.is_unverified() {
1712 let message = format!("FILE_DESCRIPTION implementation level {:?} has no implemented grammar; parsed with the 4;3 grammar", declaration.text());
1713 diagnostics.push(ParseDiagnostic {
1714 offset: description.map_or(offset, |record| record.offset),
1715 kind: ParseDiagnosticKind::ImplementationLevelUnverified,
1716 message,
1717 });
1718 }
1719 let implementation_level = declaration.level();
1720 for name in ["FILE_DESCRIPTION", "FILE_NAME"] {
1721 let count = header.iter().filter(|record| record.name == name).count();
1722 if count != 1 {
1723 push_header_metadata_diagnostic(
1724 &mut diagnostics,
1725 offset,
1726 format_args!("HEADER contains {count} {name} records; expected one"),
1727 );
1728 }
1729 }
1730 for record in header {
1731 let result = match record.name.as_str() {
1732 "FILE_DESCRIPTION" => {
1733 validate_file_description(&record.parameters, implementation_level)
1734 }
1735 "FILE_NAME" => validate_file_name(&record.parameters, implementation_level),
1736 _ => continue,
1737 };
1738 match result {
1739 Ok(()) => {}
1740 Err(ValidationError::Invalid(message)) => push_header_metadata_diagnostic(
1741 &mut diagnostics,
1742 record.offset,
1743 format_args!("{message}"),
1744 ),
1745 Err(error @ ValidationError::Resource(_)) => return Err(error),
1746 }
1747 }
1748 let schema = &schema_record.parameters;
1749 let Some(Value::List(identifiers)) = schema.first() else {
1750 return invalid("FILE_SCHEMA must contain one schema identifier list");
1751 };
1752 if schema.len() != 1 || identifiers.is_empty() {
1753 return invalid("FILE_SCHEMA has invalid or duplicate schema identifiers");
1754 }
1755 let mut admitted = Vec::new();
1756 let mut normalized_identifiers = BTreeSet::new();
1757 for value in identifiers {
1758 let identifier = match value {
1759 Value::String(bytes) => decoded_bytes(bytes, implementation_level),
1760 _ => None,
1761 };
1762 let Some(identifier) = identifier.and_then(AdmittedSchemaIdentifier::admit) else {
1763 push_header_metadata_diagnostic(&mut diagnostics, schema_record.offset, format_args!("FILE_SCHEMA contains an unusable identifier; schema selected from the readable identifiers"));
1764 continue;
1765 };
1766 let trimmed = identifier.text().trim();
1767
1768 let normalized = trimmed.to_ascii_uppercase();
1769 if !normalized_identifiers.insert(normalized) {
1770 push_header_metadata_diagnostic(
1771 &mut diagnostics,
1772 schema_record.offset,
1773 format_args!(
1774 "FILE_SCHEMA repeats an identifier; duplicate excluded from schema selection"
1775 ),
1776 );
1777 continue;
1778 }
1779 admitted.push(identifier);
1780 }
1781 if admitted.is_empty() {
1782 return invalid("FILE_SCHEMA has no usable schema identifiers");
1783 }
1784 for diagnostic in schema_object_identifier_diagnostics(&admitted, schema_record.offset) {
1785 diagnostics.push(diagnostic?);
1786 }
1787 Ok((
1788 HeaderAdmission {
1789 implementation_level: declaration,
1790 schema_identifiers: admitted,
1791 },
1792 diagnostics,
1793 ))
1794}
1795
1796fn validate_file_description(
1797 description: &[Value],
1798 implementation_level: ImplementationLevel,
1799) -> Result<(), ValidationError> {
1800 let [description_strings, implementation_level_value @ Value::String(_)] = description else {
1801 return invalid("FILE_DESCRIPTION has invalid parameters");
1802 };
1803 if !is_string_list(Some(description_strings)) {
1804 return invalid("FILE_DESCRIPTION has invalid parameters");
1805 }
1806 if !is_decodable_string_list(Some(description_strings), implementation_level)
1807 || !is_decodable_string(implementation_level_value, implementation_level)
1808 {
1809 return invalid("FILE_DESCRIPTION has invalid string encoding");
1810 }
1811 if !string_list_within_limit(Some(description_strings), implementation_level, 256)
1812 || !string_within_limit(implementation_level_value, implementation_level, 256)
1813 {
1814 return invalid("FILE_DESCRIPTION contains a string longer than 256 characters");
1815 }
1816
1817 Ok(())
1818}
1819
1820fn validate_file_name(
1821 file_name: &[Value],
1822 implementation_level: ImplementationLevel,
1823) -> Result<(), ValidationError> {
1824 let [file_name_value, file_name_timestamp, authors, organizations, preprocessor, originating_system, authorization] =
1826 file_name
1827 else {
1828 return invalid("FILE_NAME has invalid parameters");
1829 };
1830 if !matches!(file_name_value, Value::String(_))
1831 || !matches!(file_name_timestamp, Value::String(_))
1832 || !is_string_list(Some(authors))
1833 || !is_string_list(Some(organizations))
1834 || !is_string_or_omitted(Some(preprocessor))
1835 || !is_string_or_omitted(Some(originating_system))
1836 || !is_string_or_omitted(Some(authorization))
1837 {
1838 return invalid("FILE_NAME has invalid parameters");
1839 }
1840 let Some(time_stamp) = decoded_string(file_name_timestamp, implementation_level) else {
1841 return invalid("FILE_NAME has invalid string encoding");
1842 };
1843 if !is_decodable_string(file_name_value, implementation_level)
1844 || !is_decodable_string_list(Some(authors), implementation_level)
1845 || !is_decodable_string_list(Some(organizations), implementation_level)
1846 || !is_decodable_string_or_omitted(preprocessor, implementation_level)
1847 || !is_decodable_string_or_omitted(originating_system, implementation_level)
1848 || !is_decodable_string_or_omitted(authorization, implementation_level)
1849 {
1850 return invalid("FILE_NAME has invalid string encoding");
1851 }
1852 if !string_within_limit(file_name_value, implementation_level, 256)
1853 || !string_within_limit(file_name_timestamp, implementation_level, 256)
1854 || !string_list_within_limit(Some(authors), implementation_level, 256)
1855 || !string_list_within_limit(Some(organizations), implementation_level, 256)
1856 || !string_or_omitted_within_limit(preprocessor, implementation_level, 256)
1857 || !string_or_omitted_within_limit(originating_system, implementation_level, 256)
1858 || !string_or_omitted_within_limit(authorization, implementation_level, 256)
1859 {
1860 return invalid("FILE_NAME contains a string longer than 256 characters");
1861 }
1862 if !time_stamp.is_empty() && !valid_timestamp_text(&time_stamp) {
1863 return invalid("FILE_NAME has an invalid timestamp");
1864 }
1865
1866 Ok(())
1867}
1868
1869fn schema_object_identifier_diagnostics(
1872 admitted: &[AdmittedSchemaIdentifier],
1873 offset: usize,
1874) -> impl Iterator<Item = Result<ParseDiagnostic, CodecError>> + '_ {
1875 admitted
1876 .iter()
1877 .filter_map(move |identifier| identifier.out_of_range().map(|(name, component)| Ok(format!("FILE_SCHEMA identifier {name} has an out-of-range object identifier component {component}; the object identifier is not admitted"))
1878 .map(|message| ParseDiagnostic {
1879 offset,
1880 kind: ParseDiagnosticKind::SchemaObjectIdentifierOutOfRange,
1881 message,
1882 })))
1883}
1884
1885enum HeaderDataReferences {
1886 FilePopulation(BTreeSet<String>),
1887 Section(String),
1888}
1889
1890fn validate_header_sections(
1891 implementation_level: ImplementationLevel,
1892 header: &[HeaderRecord],
1893 schema_identifiers: &[String],
1894) -> Result<Vec<HeaderDataReferences>, ValidationError> {
1895 let has = |name: &str| header.iter().any(|record| record.name == name);
1896 if implementation_level == ImplementationLevel::LegacyEdition1 && has("FILE_POPULATION") {
1897 return invalid("2;1 forbids FILE_POPULATION in HEADER");
1898 }
1899 if implementation_level == ImplementationLevel::LegacyEdition1 && has("SECTION_LANGUAGE") {
1900 return invalid("2;1 forbids SECTION_LANGUAGE in HEADER");
1901 }
1902 if implementation_level == ImplementationLevel::LegacyEdition1 && has("SECTION_CONTEXT") {
1903 return invalid("2;1 forbids SECTION_CONTEXT in HEADER");
1904 }
1905 match implementation_level {
1906 ImplementationLevel::LegacyEdition2 if has("SCHEMA_POPULATION") => {
1907 return invalid("3;1 forbids SCHEMA_POPULATION in HEADER");
1908 }
1909 ImplementationLevel::Edition3Class1 if has("SCHEMA_POPULATION") => {
1910 return invalid("4;1 forbids SCHEMA_POPULATION in HEADER");
1911 }
1912 _ => {}
1913 }
1914
1915 let mut references = Vec::new();
1916 let mut user_defined = false;
1917 let mut schema_population_seen = false;
1918 let mut language_sections = BTreeSet::new();
1919 let mut context_sections = BTreeSet::new();
1920 for record in header {
1921 if matches!(
1922 record.name.as_str(),
1923 "FILE_DESCRIPTION" | "FILE_NAME" | "FILE_SCHEMA"
1924 ) {
1925 continue;
1926 }
1927 if record.name.starts_with('!') {
1928 user_defined = true;
1929 continue;
1930 }
1931 if user_defined {
1932 return invalid("built-in HEADER entities must precede user-defined entities");
1933 }
1934 match record.name.as_str() {
1935 "SCHEMA_POPULATION" => {
1936 if schema_population_seen {
1937 return invalid("HEADER contains duplicate SCHEMA_POPULATION");
1938 }
1939 schema_population_seen = true;
1940 if !valid_schema_population(&record.parameters, implementation_level) {
1941 return invalid("SCHEMA_POPULATION has invalid parameters");
1942 }
1943 }
1944 "FILE_POPULATION" => {
1945 let sections = admit_file_population(
1946 &record.parameters,
1947 schema_identifiers,
1948 implementation_level,
1949 )
1950 .map_err(|error| error.with_message("FILE_POPULATION has invalid parameters"))?;
1951 references.push(HeaderDataReferences::FilePopulation(sections));
1952 }
1953 "SECTION_LANGUAGE" => {
1954 let section = valid_section_language(&record.parameters, implementation_level)
1955 .map_err(|error| {
1956 error.with_message("SECTION_LANGUAGE has invalid parameters")
1957 })?;
1958 let section_copy = section.as_deref().map(std::borrow::ToOwned::to_owned);
1959 if !language_sections.insert(section_copy) {
1960 return invalid("HEADER contains duplicate SECTION_LANGUAGE section");
1961 }
1962 if let Some(section) = section {
1963 references.push(HeaderDataReferences::Section(section));
1964 }
1965 }
1966 "SECTION_CONTEXT" => {
1967 let section = valid_section_context(&record.parameters, implementation_level)
1968 .map_err(|error| {
1969 error.with_message("SECTION_CONTEXT has invalid parameters")
1970 })?;
1971 let section_copy = section.as_deref().map(std::borrow::ToOwned::to_owned);
1972 if !context_sections.insert(section_copy) {
1973 return invalid("HEADER contains duplicate SECTION_CONTEXT section");
1974 }
1975 if let Some(section) = section {
1976 references.push(HeaderDataReferences::Section(section));
1977 }
1978 }
1979 _ => return invalid("HEADER contains an unsupported entity"),
1980 }
1981 }
1982 Ok(references)
1983}
1984
1985fn valid_schema_population(
1986 parameters: &[Value],
1987 implementation_level: ImplementationLevel,
1988) -> bool {
1989 let [Value::List(identifications)] = parameters else {
1990 return false;
1991 };
1992 if identifications.is_empty() {
1993 return false;
1994 }
1995 for identification in identifications {
1996 let Value::List(values) = identification else {
1997 return false;
1998 };
1999 let [Value::String(address), time_stamp, digest] = values.as_slice() else {
2000 return false;
2001 };
2002 if decoded_bytes(address, implementation_level).is_none()
2003 || !valid_optional_timestamp(time_stamp, implementation_level)
2004 || !valid_optional_base64(digest, implementation_level)
2005 {
2006 return false;
2007 }
2008 }
2009 true
2010}
2011
2012fn admit_file_population(
2013 parameters: &[Value],
2014 schema_identifiers: &[String],
2015 implementation_level: ImplementationLevel,
2016) -> Result<BTreeSet<String>, ValidationError> {
2017 let [Value::String(schema), Value::String(determination), governed_sections] = parameters
2018 else {
2019 return invalid("FILE_POPULATION has invalid parameters");
2020 };
2021 let Some(schema) = decoded_bytes(schema, implementation_level) else {
2022 return invalid("FILE_POPULATION has invalid parameters");
2023 };
2024 if !valid_schema_identifier(&schema)
2025 || decoded_bytes(determination, implementation_level).is_none()
2026 || !schema_identifier_matches(schema_identifiers, &schema)
2027 {
2028 return invalid("FILE_POPULATION has invalid parameters");
2029 }
2030 match governed_sections {
2031 Value::Omitted => Ok(BTreeSet::new()),
2032 Value::List(sections) if !sections.is_empty() => {
2033 let mut names = BTreeSet::new();
2034 for section in sections {
2035 let Some(section) = decoded_string(section, implementation_level) else {
2036 return invalid("FILE_POPULATION has invalid parameters");
2037 };
2038 if !names.insert(section) {
2039 return invalid("FILE_POPULATION has invalid parameters");
2040 }
2041 }
2042 Ok(names)
2043 }
2044 _ => invalid("FILE_POPULATION has invalid parameters"),
2045 }
2046}
2047
2048fn valid_section_language(
2049 parameters: &[Value],
2050 implementation_level: ImplementationLevel,
2051) -> Result<Option<String>, ValidationError> {
2052 let [section, language] = parameters else {
2053 return invalid("SECTION_LANGUAGE has invalid parameters");
2054 };
2055 let Some(language) = decoded_string(language, implementation_level) else {
2056 return invalid("SECTION_LANGUAGE has invalid parameters");
2057 };
2058 if language.len() != 3 || !language.bytes().all(|byte| byte.is_ascii_alphabetic()) {
2059 return invalid("SECTION_LANGUAGE has invalid parameters");
2060 }
2061 valid_optional_section_name(section, implementation_level)
2062}
2063
2064fn valid_section_context(
2065 parameters: &[Value],
2066 implementation_level: ImplementationLevel,
2067) -> Result<Option<String>, ValidationError> {
2068 let [section, Value::List(contexts)] = parameters else {
2069 return invalid("SECTION_CONTEXT has invalid parameters");
2070 };
2071 if contexts.is_empty() {
2072 return invalid("SECTION_CONTEXT has invalid parameters");
2073 }
2074 for context in contexts {
2075 if decoded_string(context, implementation_level).is_none() {
2076 return invalid("SECTION_CONTEXT has invalid parameters");
2077 }
2078 }
2079 valid_optional_section_name(section, implementation_level)
2080}
2081
2082fn valid_optional_section_name(
2083 value: &Value,
2084 implementation_level: ImplementationLevel,
2085) -> Result<Option<String>, ValidationError> {
2086 match value {
2087 Value::Omitted => Ok(None),
2088 value => decoded_string(value, implementation_level)
2089 .map(Some)
2090 .ok_or(ValidationError::Invalid("invalid section name")),
2091 }
2092}
2093
2094fn is_decodable_string(value: &Value, implementation_level: ImplementationLevel) -> bool {
2095 decoded_string(value, implementation_level).is_some()
2096}
2097
2098fn is_decodable_string_list(
2099 value: Option<&Value>,
2100 implementation_level: ImplementationLevel,
2101) -> bool {
2102 let Some(Value::List(values)) = value else {
2103 return false;
2104 };
2105 if values.is_empty() {
2106 return false;
2107 }
2108 for value in values {
2109 if !is_decodable_string(value, implementation_level) {
2110 return false;
2111 }
2112 }
2113 true
2114}
2115
2116fn is_decodable_string_or_omitted(
2117 value: &Value,
2118 implementation_level: ImplementationLevel,
2119) -> bool {
2120 if matches!(value, Value::Omitted) {
2121 return true;
2122 }
2123 is_decodable_string(value, implementation_level)
2124}
2125
2126fn string_within_limit(
2127 value: &Value,
2128 implementation_level: ImplementationLevel,
2129 limit: usize,
2130) -> bool {
2131 decoded_string(value, implementation_level).is_some_and(|value| value.chars().count() <= limit)
2132}
2133
2134fn string_list_within_limit(
2135 value: Option<&Value>,
2136 implementation_level: ImplementationLevel,
2137 limit: usize,
2138) -> bool {
2139 let Some(Value::List(values)) = value else {
2140 return false;
2141 };
2142 if values.is_empty() {
2143 return false;
2144 }
2145 for value in values {
2146 if !string_within_limit(value, implementation_level, limit) {
2147 return false;
2148 }
2149 }
2150 true
2151}
2152
2153fn string_or_omitted_within_limit(
2154 value: &Value,
2155 implementation_level: ImplementationLevel,
2156 limit: usize,
2157) -> bool {
2158 if matches!(value, Value::Omitted) {
2159 return true;
2160 }
2161 string_within_limit(value, implementation_level, limit)
2162}
2163
2164fn valid_optional_timestamp(value: &Value, implementation_level: ImplementationLevel) -> bool {
2165 match value {
2166 Value::Omitted => true,
2167 Value::String(_) => decoded_string(value, implementation_level)
2168 .is_some_and(|value| valid_timestamp_text(&value)),
2169 _ => false,
2170 }
2171}
2172
2173fn valid_timestamp_text(value: &str) -> bool {
2174 let bytes = value.as_bytes();
2175 if bytes.len() < 19
2176 || bytes.get(4) != Some(&b'-')
2177 || bytes.get(7) != Some(&b'-')
2178 || bytes.get(10) != Some(&b'T')
2179 || bytes.get(13) != Some(&b':')
2180 || bytes.get(16) != Some(&b':')
2181 || !all_ascii_digits(&bytes[0..4])
2182 || !all_ascii_digits(&bytes[5..7])
2183 || !all_ascii_digits(&bytes[8..10])
2184 || !all_ascii_digits(&bytes[11..13])
2185 || !all_ascii_digits(&bytes[14..16])
2186 || !all_ascii_digits(&bytes[17..19])
2187 {
2188 return false;
2189 }
2190 let year = parse_ascii_digits(&bytes[0..4]);
2191 let month = parse_ascii_digits(&bytes[5..7]);
2192 let day = parse_ascii_digits(&bytes[8..10]);
2193 let hour = parse_ascii_digits(&bytes[11..13]);
2194 let minute = parse_ascii_digits(&bytes[14..16]);
2195 let second = parse_ascii_digits(&bytes[17..19]);
2196 let Some(days) = days_in_month(year, month) else {
2197 return false;
2198 };
2199 if day == 0
2200 || day > days
2201 || minute > 59
2202 || second > 60
2203 || hour > 24
2204 || (hour == 24 && (minute != 0 || second != 0))
2205 {
2206 return false;
2207 }
2208
2209 let mut at = 19;
2210 if matches!(bytes.get(at), Some(b'.' | b',')) {
2211 at += 1;
2212 let fraction_start = at;
2213 while bytes.get(at).is_some_and(u8::is_ascii_digit) {
2214 at += 1;
2215 }
2216 if at == fraction_start {
2217 return false;
2218 }
2219 }
2220 match bytes.get(at).copied() {
2221 None => true,
2222 Some(b'Z') => at + 1 == bytes.len(),
2223 Some(b'+' | b'-') => {
2224 at += 1;
2225 at + 5 == bytes.len()
2226 && all_ascii_digits(&bytes[at..at + 2])
2227 && bytes[at + 2] == b':'
2228 && all_ascii_digits(&bytes[at + 3..at + 5])
2229 && parse_ascii_digits(&bytes[at..at + 2]) <= 23
2230 && parse_ascii_digits(&bytes[at + 3..at + 5]) <= 59
2231 }
2232 Some(_) => false,
2233 }
2234}
2235
2236fn days_in_month(year: usize, month: usize) -> Option<usize> {
2237 let days = match month {
2238 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
2239 4 | 6 | 9 | 11 => 30,
2240 2 if year.is_multiple_of(400) || (year.is_multiple_of(4) && !year.is_multiple_of(100)) => {
2241 29
2242 }
2243 2 => 28,
2244 _ => return None,
2245 };
2246 Some(days)
2247}
2248
2249fn all_ascii_digits(bytes: &[u8]) -> bool {
2250 !bytes.is_empty() && bytes.iter().all(u8::is_ascii_digit)
2251}
2252
2253fn parse_ascii_digits(bytes: &[u8]) -> usize {
2254 bytes
2255 .iter()
2256 .fold(0, |value, byte| value * 10 + usize::from(byte - b'0'))
2257}
2258
2259fn valid_optional_base64(value: &Value, implementation_level: ImplementationLevel) -> bool {
2260 match value {
2261 Value::Omitted => true,
2262 Value::String(_) => decoded_string(value, implementation_level)
2263 .is_some_and(|value| valid_base64_text(value.as_bytes())),
2264 _ => false,
2265 }
2266}
2267
2268fn valid_base64_text(bytes: &[u8]) -> bool {
2269 if bytes.is_empty() {
2270 return false;
2271 }
2272 let mut quantum_len = 0;
2273 let mut padding = 0;
2274 let mut finished = false;
2275 for &byte in bytes {
2276 let is_alphabet = byte.is_ascii_alphanumeric() || matches!(byte, b'+' | b'/');
2277 if finished || (padding != 0 && is_alphabet) {
2278 return false;
2279 }
2280 if is_alphabet {
2281 quantum_len += 1;
2282 } else if byte == b'=' {
2283 if quantum_len < 2 || padding == 2 {
2284 return false;
2285 }
2286 padding += 1;
2287 quantum_len += 1;
2288 } else {
2289 return false;
2290 }
2291 if quantum_len == 4 {
2292 finished = padding != 0;
2293 quantum_len = 0;
2294 padding = 0;
2295 }
2296 }
2297 quantum_len == 0
2298}
2299
2300fn decoded_string(value: &Value, implementation_level: ImplementationLevel) -> Option<String> {
2301 let Value::String(bytes) = value else {
2302 return None;
2303 };
2304 decoded_bytes(bytes, implementation_level)
2305}
2306
2307fn decoded_bytes(bytes: &[u8], implementation_level: ImplementationLevel) -> Option<String> {
2308 crate::strings::decode(bytes, implementation_level).ok()
2309}
2310
2311fn schema_identifier_matches(schema_identifiers: &[String], schema_name: &str) -> bool {
2312 let trimmed = schema_name.trim();
2313 let mut schema_name = trimmed.to_owned();
2314 schema_name.make_ascii_uppercase();
2315 schema_identifiers.iter().any(|identifier| {
2316 let identifier = identifier.trim();
2317 identifier == schema_name
2318 || split_schema_identifier(identifier).is_some_and(|(name, _)| name == schema_name)
2319 })
2320}
2321
2322fn validate_header_data_references(
2323 references: &[HeaderDataReferences],
2324 data_section_names: &BTreeSet<String>,
2325) -> Result<(), &'static str> {
2326 for reference in references {
2327 match reference {
2328 HeaderDataReferences::FilePopulation(sections) => {
2329 if sections
2330 .iter()
2331 .any(|section| !data_section_names.contains(section))
2332 {
2333 return Err("FILE_POPULATION names an unknown DATA section");
2334 }
2335 }
2336 HeaderDataReferences::Section(section) => {
2337 if !data_section_names.contains(section) {
2338 return Err("header section reference names an unknown DATA section");
2339 }
2340 }
2341 }
2342 }
2343 Ok(())
2344}
2345
2346fn valid_data_parameters(
2347 parameters: &[Value],
2348 schema_identifiers: &[String],
2349 implementation_level: ImplementationLevel,
2350 section_names: &mut BTreeSet<String>,
2351) -> Result<(), ValidationError> {
2352 let [Value::String(section_name), Value::List(schema)] = parameters else {
2353 return invalid("DATA section parameters must contain a name and one schema");
2354 };
2355 let [Value::String(schema_name)] = schema.as_slice() else {
2356 return invalid("DATA section parameters must contain a name and one schema");
2357 };
2358 let Some(section_name) = decoded_bytes(section_name, implementation_level) else {
2359 return invalid("DATA section parameters contain an invalid string");
2360 };
2361 if !section_names.insert(section_name) {
2362 return invalid("DATA section names must be unique");
2363 }
2364 let Some(schema_name) = decoded_bytes(schema_name, implementation_level) else {
2365 return invalid("DATA section parameters contain an invalid string");
2366 };
2367 if !valid_schema_identifier(&schema_name)
2368 || !schema_identifier_matches(schema_identifiers, &schema_name)
2369 {
2370 return invalid("DATA section schema is not listed in FILE_SCHEMA");
2371 }
2372 Ok(())
2373}
2374
2375fn schema_names_for_matching(
2377 admitted: &[AdmittedSchemaIdentifier],
2378) -> std::vec::Vec<std::string::String> {
2379 let mut names = Vec::new();
2380 for identifier in admitted {
2381 let source = identifier.text();
2382 let mut name = source.to_owned();
2383 name.make_ascii_uppercase();
2384 names.push(name);
2385 }
2386 names
2387}
2388
2389fn is_string_list(value: Option<&Value>) -> bool {
2390 matches!(
2391 value,
2392 Some(Value::List(values))
2393 if !values.is_empty() && values.iter().all(|value| matches!(value, Value::String(_)))
2394 )
2395}
2396
2397fn is_string_or_omitted(value: Option<&Value>) -> bool {
2398 matches!(value, Some(Value::String(_) | Value::Omitted))
2399}
2400
2401fn valid_anchor_name(name: &str) -> bool {
2402 !name.is_empty() && name.bytes().any(|byte| !byte.is_ascii_digit())
2403}
2404
2405fn is_anchor_item(value: &Value) -> bool {
2406 match value {
2407 Value::Reference(_)
2408 | Value::ExternalReference(_)
2409 | Value::ConstantEntity(_)
2410 | Value::ExpressValueConstant(_)
2411 | Value::Integer(_)
2412 | Value::Real(_)
2413 | Value::Enumeration(_)
2414 | Value::String(_)
2415 | Value::Binary(_)
2416 | Value::Resource(_)
2417 | Value::Omitted => true,
2418 Value::List(values) => values.iter().all(is_anchor_item),
2419 Value::Derived | Value::Typed(_, _) | Value::UninterpretedLiteral => false,
2420 }
2421}
2422
2423#[derive(Debug)]
2424enum ResolveError {
2425 Syntax(String),
2426 Bounded(String),
2429 Resource(CodecError),
2430}
2431
2432impl ResolveError {
2433 fn into_parse_error(self, offset: usize) -> ParseError {
2434 match self {
2435 Self::Syntax(message) | Self::Bounded(message) => {
2436 ParseError::Syntax { offset, message }
2437 }
2438 Self::Resource(error) => ParseError::Resource(error),
2439 }
2440 }
2441}
2442
2443#[derive(Default)]
2446struct BoundedExpansions {
2447 anchors: BTreeMap<String, String>,
2448 records: BTreeMap<u64, String>,
2449}
2450
2451#[derive(Default)]
2454struct ExpansionOmissions {
2455 anchors: BTreeSet<String>,
2456 records: BTreeSet<u64>,
2457}
2458
2459impl From<String> for ResolveError {
2460 fn from(message: String) -> Self {
2461 Self::Syntax(message)
2462 }
2463}
2464
2465impl From<&str> for ResolveError {
2466 fn from(message: &str) -> Self {
2467 Self::Syntax(message.into())
2468 }
2469}
2470
2471impl From<CodecError> for ResolveError {
2472 fn from(error: CodecError) -> Self {
2473 Self::Resource(error)
2474 }
2475}
2476
2477enum KnownOutcome {
2479 Fits,
2481 Bounded(String),
2483 Unknown,
2485}
2486
2487#[derive(Default)]
2493struct ExpansionBounds {
2494 node_limit_at: Option<usize>,
2496 other_at: Option<(usize, String)>,
2499}
2500
2501impl ExpansionBounds {
2502 fn record(&mut self, budget: usize, bound: &str, node_limit: &str) {
2503 if bound == node_limit {
2504 self.node_limit_at = Some(self.node_limit_at.map_or(budget, |at| at.max(budget)));
2505 } else if self.other_at.as_ref().is_none_or(|(at, _)| budget < *at) {
2506 self.other_at = Some((budget, bound.to_owned()));
2507 }
2508 }
2509
2510 fn bound_at(&self, budget: usize, node_limit: &str) -> Option<String> {
2511 if self.node_limit_at.is_some_and(|at| budget <= at) {
2512 return Some(node_limit.to_owned());
2513 }
2514 self.other_at
2515 .as_ref()
2516 .filter(|(at, _)| *at <= budget)
2517 .map(|(_, bound)| bound.clone())
2518 }
2519}
2520
2521struct AnchorResolver<'a, 'ctx, 'arena> {
2522 anchors: &'a BTreeMap<String, Value>,
2523 memo: BTreeMap<&'a str, (Value, usize, u64)>,
2525 bounds: BTreeMap<&'a str, ExpansionBounds>,
2526 remaining_nodes: usize,
2527 discard_allowance: Option<usize>,
2529 item_nodes: usize,
2531 payload: PayloadAllowance,
2532 budget: &'ctx DecodeContext<'arena>,
2533}
2534
2535impl<'a, 'ctx, 'arena> AnchorResolver<'a, 'ctx, 'arena> {
2536 const MAX_EXPANDED_NODES: usize = 1_000_000;
2538 const MAX_DISCARDED_EXPANDED_NODES: usize = 16 * Self::MAX_EXPANDED_NODES;
2541
2542 fn new(
2543 anchors: &'a BTreeMap<String, Value>,
2544 payload_limit: u64,
2545 budget: &'ctx DecodeContext<'arena>,
2546 ) -> Self {
2547 Self {
2548 anchors,
2549 memo: BTreeMap::new(),
2550 bounds: BTreeMap::new(),
2551 remaining_nodes: Self::MAX_EXPANDED_NODES,
2552 discard_allowance: Some(Self::MAX_DISCARDED_EXPANDED_NODES),
2553 item_nodes: 0,
2554 payload: PayloadAllowance::new(payload_limit),
2555 budget,
2556 }
2557 }
2558
2559 fn resolve_item<'v>(
2563 &mut self,
2564 values: impl Iterator<Item = &'v mut Value>,
2565 ) -> Result<Option<String>, ResolveError> {
2566 let values = values.collect::<Vec<_>>();
2567 match self.known_outcome(&values) {
2568 KnownOutcome::Bounded(bound) => return Ok(Some(bound)),
2569 KnownOutcome::Unknown if self.discard_allowance.is_none() => {
2570 return Ok(Some(Self::discard_limit_bound()));
2571 }
2572 KnownOutcome::Fits | KnownOutcome::Unknown => {}
2573 }
2574 let remaining = self.remaining_nodes;
2575 self.item_nodes = 0;
2576 for value in values {
2577 match self.resolve_root(value) {
2578 Ok(resolved) => *value = resolved,
2579 Err(ResolveError::Bounded(bound)) => {
2580 self.remaining_nodes = remaining;
2581 self.discard_allowance = self
2582 .discard_allowance
2583 .and_then(|allowance| allowance.checked_sub(self.item_nodes));
2584 return Ok(Some(bound));
2585 }
2586 Err(error) => return Err(error),
2587 }
2588 }
2589 Ok(None)
2590 }
2591
2592 fn known_outcome(&self, values: &[&mut Value]) -> KnownOutcome {
2595 let node_limit = Self::node_limit_bound();
2596 let mut budget = self.remaining_nodes;
2597 let mut payload = self.payload.remaining;
2598 let mut pending = values
2599 .iter()
2600 .rev()
2601 .map(|value| &**value)
2602 .collect::<Vec<_>>();
2603 while let Some(value) = pending.pop() {
2604 match value {
2605 Value::Resource(name) => {
2606 let Some(name) = name
2607 .strip_prefix('#')
2608 .filter(|name| self.anchors.contains_key(*name))
2609 else {
2610 continue;
2611 };
2612 if let Some((_, nodes, octets)) = self.memo.get(name) {
2613 let Some(rest) = budget.checked_sub(*nodes) else {
2614 return KnownOutcome::Bounded(node_limit);
2615 };
2616 let Some(rest_payload) = payload.checked_sub(*octets) else {
2617 return KnownOutcome::Bounded(self.payload.bound("anchor"));
2618 };
2619 budget = rest;
2620 payload = rest_payload;
2621 } else if let Some(bound) = self
2622 .bounds
2623 .get(name)
2624 .and_then(|bounds| bounds.bound_at(budget, &node_limit))
2625 {
2626 return KnownOutcome::Bounded(bound);
2627 } else {
2628 return KnownOutcome::Unknown;
2629 }
2630 }
2631 Value::List(values) => pending.extend(values.iter().rev()),
2632 Value::Typed(_, value) => pending.push(value),
2633 _ => {}
2634 }
2635 }
2636 KnownOutcome::Fits
2637 }
2638
2639 fn resolve_root(&mut self, value: &Value) -> Result<Value, ResolveError> {
2640 let mut stack = Vec::new();
2641 let (value, _, expanded_nodes) = self.resolve(value, &mut stack, self.remaining_nodes)?;
2642 self.remaining_nodes = self
2643 .remaining_nodes
2644 .checked_sub(expanded_nodes)
2645 .ok_or_else(Self::node_limit_error)?;
2646 Ok(value)
2647 }
2648
2649 fn node_limit_bound() -> String {
2650 format!(
2651 "anchor expansion exceeds {} value nodes",
2652 Self::MAX_EXPANDED_NODES
2653 )
2654 }
2655
2656 fn node_limit_error() -> ResolveError {
2657 ResolveError::Bounded(Self::node_limit_bound())
2658 }
2659
2660 fn discard_limit_bound() -> String {
2661 format!(
2662 "anchor expansions omitted at the node limit already copied {} value nodes",
2663 Self::MAX_DISCARDED_EXPANDED_NODES
2664 )
2665 }
2666
2667 fn count_item_nodes(&mut self, nodes: usize) -> Result<(), ResolveError> {
2668 self.item_nodes = self
2669 .item_nodes
2670 .checked_add(nodes)
2671 .ok_or_else(Self::node_limit_error)?;
2672 Ok(())
2673 }
2674
2675 fn charge_payload(&mut self, octets: u64) -> Result<(), ResolveError> {
2676 if self.payload.charge(octets) {
2677 Ok(())
2678 } else {
2679 Err(ResolveError::Bounded(self.payload.bound("anchor")))
2680 }
2681 }
2682
2683 fn resolve(
2684 &mut self,
2685 value: &Value,
2686 stack: &mut Vec<&'a str>,
2687 budget: usize,
2688 ) -> Result<(Value, usize, usize), ResolveError> {
2689 let _nested = self.budget.enter_nested("step_anchor_reference")?;
2690 self.count_item_nodes(1)?;
2691 if let Value::Resource(name) = value {
2692 if let Some((name, source)) = name
2693 .strip_prefix('#')
2694 .and_then(|name| self.anchors.get_key_value(name))
2695 {
2696 let name = name.as_str();
2697 if let Some((value, nodes, octets)) = self.memo.get(name) {
2698 if *nodes > budget {
2699 return Err(Self::node_limit_error());
2700 }
2701 if !self.payload.charge(*octets) {
2702 return Err(ResolveError::Bounded(self.payload.bound("anchor")));
2703 }
2704 let (value, nodes) = (try_clone_value(value, self.budget)?, *nodes);
2705 self.count_item_nodes(nodes)?;
2706 return Ok((value, nodes, nodes));
2707 }
2708 if stack.contains(&name) {
2709 let message = format!("cyclic anchor binding <{name}>");
2710 return Err(message.into());
2711 }
2712 let top_level = stack.is_empty();
2713 let expanded = self.expand(name, source, stack, budget);
2714 if top_level {
2715 if let Err(ResolveError::Bounded(bound)) = &expanded {
2716 self.bounds.entry(name).or_default().record(
2717 budget,
2718 bound,
2719 &Self::node_limit_bound(),
2720 );
2721 }
2722 }
2723 return expanded;
2724 }
2725 }
2726 match value {
2727 Value::List(values) => {
2728 let mut nodes = 1usize;
2729 let mut expanded_nodes = 0usize;
2730 let mut resolved = Vec::new();
2731 for value in values {
2732 let remaining = budget
2733 .checked_sub(expanded_nodes)
2734 .ok_or_else(Self::node_limit_error)?;
2735 let (value, child_nodes, child_expanded_nodes) =
2736 self.resolve(value, stack, remaining)?;
2737 nodes = nodes
2738 .checked_add(child_nodes)
2739 .ok_or_else(Self::node_limit_error)?;
2740 expanded_nodes = expanded_nodes
2741 .checked_add(child_expanded_nodes)
2742 .ok_or_else(Self::node_limit_error)?;
2743 resolved.push(value);
2744 }
2745 Ok((Value::List(resolved), nodes, expanded_nodes))
2746 }
2747 Value::Typed(name, nested) => {
2748 if !stack.is_empty() {
2749 self.charge_payload(node_payload(value))?;
2750 }
2751 let (value, nodes, expanded_nodes) = self.resolve(nested, stack, budget)?;
2752 let value = Value::Typed(name.to_owned(), Box::new(value));
2753 Ok((
2754 value,
2755 nodes.checked_add(1).ok_or_else(Self::node_limit_error)?,
2756 expanded_nodes,
2757 ))
2758 }
2759 value => {
2760 if !stack.is_empty() {
2761 self.charge_payload(node_payload(value))?;
2762 }
2763 Ok((try_clone_value(value, self.budget)?, 1, 0))
2764 }
2765 }
2766 }
2767
2768 fn expand(
2770 &mut self,
2771 name: &'a str,
2772 source: &Value,
2773 stack: &mut Vec<&'a str>,
2774 budget: usize,
2775 ) -> Result<(Value, usize, usize), ResolveError> {
2776 let counted = value_node_count(source, Self::MAX_EXPANDED_NODES, self.budget);
2777 self.count_item_nodes(
2778 counted
2779 .as_ref()
2780 .map_or(Self::MAX_EXPANDED_NODES, |nodes| *nodes),
2781 )?;
2782 counted?;
2783 let payload_before = self.payload.remaining;
2784 stack.push(name);
2785 let resolved = self.resolve(source, stack, budget);
2786 stack.pop();
2787 let (value, nodes, _) = resolved?;
2788 if nodes > budget {
2789 return Err(Self::node_limit_error());
2790 }
2791 let octets = payload_before - self.payload.remaining;
2792 let memo = try_clone_value(&value, self.budget)?;
2793 self.memo.insert(name, (memo, nodes, octets));
2794 Ok((value, nodes, nodes))
2795 }
2796}
2797
2798struct ReferenceResolver<'a, 'ctx, 'arena> {
2799 bindings: BTreeMap<ReferenceName, &'a str>,
2800 anchors: &'a BTreeMap<String, Value>,
2801 omitted_anchors: &'a BTreeSet<String>,
2802 stack: Vec<ReferenceName>,
2803 used: BTreeMap<ReferenceName, (usize, u64)>,
2806 bounds: BTreeMap<ReferenceName, ExpansionBounds>,
2807 remaining_nodes: usize,
2808 discard_allowance: Option<usize>,
2810 item_nodes: usize,
2812 payload: PayloadAllowance,
2813 budget: &'ctx DecodeContext<'arena>,
2814}
2815
2816impl<'a, 'ctx, 'arena> ReferenceResolver<'a, 'ctx, 'arena> {
2817 const MAX_MATERIALIZED_NODES: usize = 1_000_000;
2820 const MAX_DISCARDED_MATERIALIZED_NODES: usize = 16 * Self::MAX_MATERIALIZED_NODES;
2823
2824 fn new(
2825 references: &'a [ReferenceEntry],
2826 anchors: &'a BTreeMap<String, Value>,
2827 omitted_anchors: &'a BTreeSet<String>,
2828 payload_limit: u64,
2829 budget: &'ctx DecodeContext<'arena>,
2830 ) -> Self {
2831 let bindings = references
2832 .iter()
2833 .map(|reference| (reference.name, reference.uri.as_str()))
2834 .collect();
2835 Self {
2836 bindings,
2837 anchors,
2838 omitted_anchors,
2839 stack: Vec::new(),
2840 used: BTreeMap::new(),
2841 bounds: BTreeMap::new(),
2842 remaining_nodes: Self::MAX_MATERIALIZED_NODES,
2843 discard_allowance: Some(Self::MAX_DISCARDED_MATERIALIZED_NODES),
2844 item_nodes: 0,
2845 payload: PayloadAllowance::new(payload_limit),
2846 budget,
2847 }
2848 }
2849
2850 fn node_limit_bound() -> String {
2851 format!(
2852 "external reference expansion exceeds {} value nodes",
2853 Self::MAX_MATERIALIZED_NODES
2854 )
2855 }
2856
2857 fn node_limit_error() -> ResolveError {
2858 ResolveError::Bounded(Self::node_limit_bound())
2859 }
2860
2861 fn omitted_anchor_bound(key: ReferenceName, fragment: &str) -> String {
2862 format!("REFERENCE {key} names anchor <{fragment}>, omitted at its expansion limit")
2863 }
2864
2865 fn discard_limit_bound() -> String {
2866 format!(
2867 "external reference expansions omitted at the node limit already copied {} value nodes",
2868 Self::MAX_DISCARDED_MATERIALIZED_NODES
2869 )
2870 }
2871
2872 fn resolve_item<'v>(
2876 &mut self,
2877 values: impl Iterator<Item = &'v mut Value>,
2878 ) -> Result<Option<String>, ResolveError> {
2879 let values = values.collect::<Vec<_>>();
2880 match self.known_outcome(&values) {
2881 KnownOutcome::Bounded(bound) => return Ok(Some(bound)),
2882 KnownOutcome::Unknown if self.discard_allowance.is_none() => {
2883 return Ok(Some(Self::discard_limit_bound()));
2884 }
2885 KnownOutcome::Fits | KnownOutcome::Unknown => {}
2886 }
2887 let remaining = self.remaining_nodes;
2888 self.item_nodes = 0;
2889 for value in values {
2890 match self.resolve_value(value) {
2891 Ok(resolved) => *value = resolved,
2892 Err(ResolveError::Bounded(bound)) => {
2893 self.remaining_nodes = remaining;
2894 self.discard_allowance = self
2895 .discard_allowance
2896 .and_then(|allowance| allowance.checked_sub(self.item_nodes));
2897 return Ok(Some(bound));
2898 }
2899 Err(error) => return Err(error),
2900 }
2901 }
2902 Ok(None)
2903 }
2904
2905 fn known_outcome(&self, values: &[&mut Value]) -> KnownOutcome {
2908 let node_limit = Self::node_limit_bound();
2909 let mut budget = self.remaining_nodes;
2910 let mut payload = self.payload.remaining;
2911 let mut pending = values
2912 .iter()
2913 .rev()
2914 .map(|value| &**value)
2915 .collect::<Vec<_>>();
2916 while let Some(value) = pending.pop() {
2917 let key = match value {
2918 Value::Reference(id) => ReferenceName::Entity(*id),
2919 Value::ExternalReference(id) => ReferenceName::Value(*id),
2920 Value::List(values) => {
2921 pending.extend(values.iter().rev());
2922 continue;
2923 }
2924 Value::Typed(_, value) => {
2925 pending.push(value);
2926 continue;
2927 }
2928 _ => continue,
2929 };
2930 let Some(fragment) = self
2931 .bindings
2932 .get(&key)
2933 .and_then(|uri| uri.split_once('#'))
2934 .and_then(|(path, fragment)| path.is_empty().then_some(fragment))
2935 else {
2936 continue;
2937 };
2938 if self.omitted_anchors.contains(fragment) {
2939 return KnownOutcome::Bounded(Self::omitted_anchor_bound(key, fragment));
2940 }
2941 if !self.anchors.contains_key(fragment) {
2942 continue;
2943 }
2944 if let Some((nodes, octets)) = self.used.get(&key) {
2945 let Some(rest) = budget.checked_sub(*nodes) else {
2946 return KnownOutcome::Bounded(node_limit);
2947 };
2948 let Some(rest_payload) = payload.checked_sub(*octets) else {
2949 return KnownOutcome::Bounded(self.payload.bound("external reference"));
2950 };
2951 budget = rest;
2952 payload = rest_payload;
2953 } else if let Some(bound) = self
2954 .bounds
2955 .get(&key)
2956 .and_then(|bounds| bounds.bound_at(budget, &node_limit))
2957 {
2958 return KnownOutcome::Bounded(bound);
2959 } else {
2960 return KnownOutcome::Unknown;
2961 }
2962 }
2963 KnownOutcome::Fits
2964 }
2965
2966 fn clone_leaf(&mut self, value: &Value) -> Result<Value, ResolveError> {
2967 self.consume_materialized_node(value)?;
2968 Ok(try_clone_value(value, self.budget)?)
2969 }
2970
2971 fn resolve_value(&mut self, value: &Value) -> Result<Value, ResolveError> {
2972 let _nested = self.budget.enter_nested("step_reference_expansion")?;
2973 self.item_nodes = self
2974 .item_nodes
2975 .checked_add(1)
2976 .ok_or_else(Self::node_limit_error)?;
2977 match value {
2978 Value::Reference(id) => self.resolve_occurrence(ReferenceName::Entity(*id), value),
2979 Value::ExternalReference(id) => {
2980 self.resolve_occurrence(ReferenceName::Value(*id), value)
2981 }
2982 Value::List(values) => {
2983 self.consume_materialized_node(value)?;
2984 if !self.stack.is_empty() && values.len() > self.remaining_nodes {
2985 return Err(Self::node_limit_error());
2986 }
2987 let mut resolved = Vec::new();
2988 for value in values {
2989 resolved.push(self.resolve_value(value)?);
2990 }
2991 Ok(Value::List(resolved))
2992 }
2993 Value::Typed(name, nested) => {
2994 self.consume_materialized_node(value)?;
2995 let resolved = self.resolve_value(nested)?;
2996 Ok(Value::Typed(name.to_owned(), Box::new(resolved)))
2997 }
2998 _ => self.clone_leaf(value),
2999 }
3000 }
3001
3002 fn resolve_occurrence(
3003 &mut self,
3004 key: ReferenceName,
3005 original: &Value,
3006 ) -> Result<Value, ResolveError> {
3007 let Some(uri) = self.bindings.get(&key).copied() else {
3008 return self.clone_leaf(original);
3009 };
3010 let Some((path, fragment)) = uri.split_once('#') else {
3011 return self.clone_leaf(&Value::Omitted);
3012 };
3013 if !path.is_empty() {
3014 return self.clone_leaf(original);
3015 }
3016 if self.stack.contains(&key) {
3017 return self.clone_leaf(&Value::Omitted);
3018 }
3019 if self.omitted_anchors.contains(fragment) {
3020 return Err(ResolveError::Bounded(Self::omitted_anchor_bound(
3021 key, fragment,
3022 )));
3023 }
3024 let Some(anchor) = self.anchors.get(fragment) else {
3025 return if is_uuid_fragment(fragment) {
3026 self.clone_leaf(original)
3027 } else {
3028 self.clone_leaf(&Value::Omitted)
3029 };
3030 };
3031 let top_level = self.stack.is_empty();
3032 let remaining = self.remaining_nodes;
3033 let payload_before = self.payload.remaining;
3034 self.stack.push(key);
3035 let resolved = self.resolve_value(anchor);
3036 self.stack.pop();
3037 if top_level {
3038 match &resolved {
3039 Ok(_) => {
3040 if let Some(nodes) = remaining.checked_sub(self.remaining_nodes) {
3041 let octets = payload_before - self.payload.remaining;
3042 self.used.insert(key, (nodes, octets));
3043 }
3044 }
3045 Err(ResolveError::Bounded(bound)) => {
3046 self.bounds.entry(key).or_default().record(
3047 remaining,
3048 bound,
3049 &Self::node_limit_bound(),
3050 );
3051 }
3052 Err(_) => {}
3053 }
3054 }
3055 let resolved = resolved?;
3056 if let Value::Resource(uri) = &resolved {
3057 return if uri.contains('#') {
3058 self.clone_leaf(original)
3059 } else {
3060 self.clone_leaf(&Value::Omitted)
3061 };
3062 }
3063 if !reference_target_matches(key, &resolved) {
3064 return self.clone_leaf(&Value::Omitted);
3065 }
3066 Ok(resolved)
3067 }
3068
3069 fn consume_materialized_node(&mut self, value: &Value) -> Result<(), ResolveError> {
3072 if !self.stack.is_empty() {
3073 self.remaining_nodes = self
3074 .remaining_nodes
3075 .checked_sub(1)
3076 .ok_or_else(Self::node_limit_error)?;
3077 if !self.payload.charge(node_payload(value)) {
3078 return Err(ResolveError::Bounded(
3079 self.payload.bound("external reference"),
3080 ));
3081 }
3082 }
3083 Ok(())
3084 }
3085}
3086
3087fn resolve_local_references(
3090 anchors: &mut [AnchorEntry],
3091 records: &mut BTreeMap<u64, RawRecord>,
3092 references: &[ReferenceEntry],
3093 omitted_anchors: &BTreeSet<String>,
3094 payload_limit: u64,
3095 budget: &DecodeContext<'_>,
3096) -> Result<BoundedExpansions, ParseError> {
3097 let mut bounded = BoundedExpansions::default();
3098 if references.is_empty() {
3099 return Ok(bounded);
3100 }
3101
3102 let mut anchor_bindings = BTreeMap::new();
3103 for anchor in anchors.iter() {
3104 let value = try_clone_value(&anchor.value, budget)?;
3105 anchor_bindings.insert(anchor.name.clone(), value);
3106 }
3107 let mut resolver = ReferenceResolver::new(
3108 references,
3109 &anchor_bindings,
3110 omitted_anchors,
3111 payload_limit,
3112 budget,
3113 );
3114 for anchor in anchors {
3115 if let Some(bound) = resolver
3116 .resolve_item(anchor.values_mut())
3117 .map_err(|error| error.into_parse_error(0))?
3118 {
3119 bounded.anchors.insert(anchor.name.clone(), bound);
3120 }
3121 }
3122 for (&id, record) in records.iter_mut() {
3123 let offset = record.span.start;
3124 if let Some(bound) = resolver
3125 .resolve_item(record.values_mut())
3126 .map_err(|error| error.into_parse_error(offset))?
3127 {
3128 bounded.records.insert(id, bound);
3129 }
3130 }
3131 Ok(bounded)
3132}
3133
3134fn reference_target_matches(name: ReferenceName, value: &Value) -> bool {
3135 match name {
3136 ReferenceName::Entity(_) => matches!(value, Value::Reference(_) | Value::ConstantEntity(_)),
3137 ReferenceName::Value(_) => !matches!(
3138 value,
3139 Value::Reference(_) | Value::ConstantEntity(_) | Value::Resource(_)
3140 ),
3141 }
3142}
3143
3144fn is_uuid_fragment(fragment: &str) -> bool {
3145 fragment.len() == 36
3146 && fragment.as_bytes().iter().enumerate().all(|(index, byte)| {
3147 matches!(index, 8 | 13 | 18 | 23)
3148 .then_some(*byte == b'-')
3149 .unwrap_or_else(|| byte.is_ascii_hexdigit())
3150 })
3151}
3152
3153fn value_node_count(
3156 value: &Value,
3157 limit: usize,
3158 budget: &DecodeContext<'_>,
3159) -> Result<usize, ResolveError> {
3160 fn visit(
3161 value: &Value,
3162 remaining: &mut usize,
3163 limit: usize,
3164 budget: &DecodeContext<'_>,
3165 ) -> Result<(), ResolveError> {
3166 let _depth_guard = budget.enter_nested("step_value_node_count")?;
3167 *remaining = remaining.checked_sub(1).ok_or_else(|| {
3168 ResolveError::Bounded(format!("anchor value exceeds {limit} value nodes"))
3169 })?;
3170 match value {
3171 Value::List(values) => {
3172 for child in values {
3173 visit(child, remaining, limit, budget)?;
3174 }
3175 }
3176 Value::Typed(_, child) => visit(child, remaining, limit, budget)?,
3177 _ => {}
3178 }
3179 Ok(())
3180 }
3181
3182 let mut remaining = limit;
3183 visit(value, &mut remaining, limit, budget)?;
3184 Ok(limit - remaining)
3185}
3186
3187fn references(
3188 value: &Value,
3189 entity_out: &mut Vec<u64>,
3190 value_out: &mut Vec<u64>,
3191 budget: &DecodeContext<'_>,
3192) -> Result<(), ParseError> {
3193 recovery::visit_references(value, budget, &mut |id, value_instance| {
3194 if value_instance {
3195 value_out.push(id);
3196 } else {
3197 entity_out.push(id);
3198 }
3199 Ok(())
3200 })
3201}
3202
3203fn contains_class3_occurrence(value: &Value) -> bool {
3204 match value {
3205 Value::ExternalReference(_) | Value::ConstantEntity(_) | Value::ExpressValueConstant(_) => {
3206 true
3207 }
3208 Value::List(values) => values.iter().any(contains_class3_occurrence),
3209 Value::Typed(_, value) => contains_class3_occurrence(value),
3210 _ => false,
3211 }
3212}
3213
3214fn contains_resource_value(value: &Value) -> bool {
3215 match value {
3216 Value::Resource(_) => true,
3217 Value::List(values) => values.iter().any(contains_resource_value),
3218 Value::Typed(_, value) => contains_resource_value(value),
3219 _ => false,
3220 }
3221}
3222
3223#[cfg(test)]
3224mod tests;