Skip to main content

cadmpeg_codec_step/
parse.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Generic Part 21 record-graph parser.
3//!
4//! Recoverable defects omit bounded records and their dependents. A loss
5//! reports each omission, and exact source spans remain available for fidelity.
6
7use 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/// One parsed Part 21 parameter value.
34#[derive(Debug, Clone, PartialEq)]
35pub(crate) enum Value {
36    /// Reference to a DATA entity instance.
37    Reference(u64),
38    /// Reference to an externally defined value instance.
39    ExternalReference(u64),
40    /// Reference to an EXPRESS entity constant.
41    ConstantEntity(String),
42    /// Reference to an EXPRESS value constant.
43    ExpressValueConstant(String),
44    /// Signed integer value.
45    Integer(i64),
46    /// Real value.
47    Real(FiniteReal),
48    /// A metadata literal whose exact bytes remain in its source record.
49    UninterpretedLiteral,
50    /// Enumeration or logical name without delimiter dots.
51    Enumeration(String),
52    /// Raw string-token bytes before Part 21 escape decoding.
53    String(Vec<u8>),
54    /// Decoded binary literal and final-byte significant-bit boundary.
55    Binary(BinaryValue),
56    /// Edition-3 resource value.
57    Resource(String),
58    /// Omitted optional value `$`.
59    Omitted,
60    /// Derived value `*`.
61    Derived,
62    /// Ordered aggregate values.
63    List(Vec<Value>),
64    /// Standard or user-defined type name and its single wrapped parameter.
65    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
97/// Octets of string, binary and name text that a copy of `value`'s own node
98/// allocates, excluding nested values.
99fn 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
120/// Payload octets that one anchor or REFERENCE expansion pass may copy out
121/// of anchors for each input byte, on top of [`EXPANDED_PAYLOAD_BASE`].
122const MAX_EXPANDED_PAYLOAD_PER_INPUT_BYTE: u64 = 16;
123/// Payload octets that one expansion pass may copy out of anchors at any
124/// input size.
125const EXPANDED_PAYLOAD_BASE: u64 = 16 * 1024 * 1024;
126
127/// Payload octets that one expansion pass may still copy out of anchors.
128/// Copies that a bounded item discards stay charged, so the allowance bounds
129/// the copying work as well as the memory the pass holds.
130#[derive(Debug)]
131struct PayloadAllowance {
132    limit: u64,
133    remaining: u64,
134}
135
136impl PayloadAllowance {
137    /// The allowance of an input of `input_len` bytes, or `None` when it
138    /// does not fit in `u64`.
139    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    /// Charges `octets` before they are copied; false leaves the allowance
153    /// unchanged.
154    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/// One simple entity leaf within an entity instance.
173#[derive(Debug, Clone, PartialEq)]
174pub(crate) struct PartialRecord {
175    /// Uppercase standard or `!`-prefixed user-defined entity name.
176    pub(crate) name: String,
177    /// Explicit external-mapping parameters.
178    pub(crate) parameters: RecordParameters,
179}
180
181pub(crate) mod partials {
182    use super::PartialRecord;
183
184    /// The nonempty partial population of one entity instance.
185    #[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        /// Builds the inline population of one leaf without a heap collection.
196        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        /// The first partial record, which always exists.
218        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/// One DATA entity instance with its exact source extent.
281#[derive(Debug, Clone, PartialEq)]
282pub(crate) struct RawRecord {
283    /// One leaf for a simple instance or all leaves for a complex instance.
284    pub(crate) partials: partials::RecordPartials,
285    /// Half-open byte range from instance name through semicolon.
286    pub(crate) span: Range<usize>,
287}
288
289impl RawRecord {
290    /// Every parameter of every partial, in source order.
291    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/// One entity-like record in the HEADER section.
299#[derive(Debug, Clone, PartialEq)]
300pub(crate) struct HeaderRecord {
301    /// Header record name.
302    pub(crate) name: String,
303    /// Header record parameters.
304    pub(crate) parameters: Vec<Value>,
305    /// Byte offset of the record name in the source.
306    pub(crate) offset: usize,
307    /// Byte offset after the terminating semicolon.
308    pub(crate) end: usize,
309}
310
311/// One DATA section and the source extent of its population.
312#[derive(Debug, Clone, PartialEq)]
313pub(crate) struct DataSection {
314    /// Edition-3 DATA section parameters.
315    pub(crate) parameters: Vec<Value>,
316    /// Half-open byte range of the DATA population, excluding ENDSEC.
317    pub(crate) span: Range<usize>,
318}
319
320/// One edition-3 ANCHOR binding.
321#[derive(Debug, Clone, PartialEq)]
322pub(crate) struct AnchorEntry {
323    /// Local resource name.
324    pub(crate) name: String,
325    /// Value bound to the resource name.
326    pub(crate) value: Value,
327    /// Ordered metadata tags attached to the binding.
328    tags: Vec<AnchorTag>,
329}
330
331impl AnchorEntry {
332    /// The bound value followed by each tag value.
333    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/// One edition-3 metadata tag attached to an ANCHOR binding.
343#[derive(Debug, Clone, PartialEq)]
344struct AnchorTag {
345    /// Tag name, preserving source case.
346    name: String,
347    /// Tag value.
348    value: Value,
349}
350
351/// An admitted entity or value occurrence name in a REFERENCE binding.
352#[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/// One edition-3 external REFERENCE binding.
368#[derive(Debug, Clone, PartialEq, Eq)]
369pub(crate) struct ReferenceEntry {
370    /// External entity or value occurrence name such as `#123`.
371    pub(crate) name: ReferenceName,
372    /// External resource URI.
373    pub(crate) uri: String,
374}
375
376/// Parsed exchange structure and global DATA record graph.
377#[derive(Debug, Clone, PartialEq)]
378pub(crate) struct Exchange {
379    /// HEADER records in source order.
380    header: Vec<HeaderRecord>,
381    /// ANCHOR bindings in source order.
382    anchors: Vec<AnchorEntry>,
383    /// REFERENCE bindings in source order.
384    references: Vec<ReferenceEntry>,
385    /// DATA sections in source order.
386    data: Vec<DataSection>,
387    /// Complete SIGNATURE section byte ranges in source order.
388    signatures: Vec<Range<usize>>,
389    /// DATA instances indexed across every DATA section.
390    records: BTreeMap<u64, RawRecord>,
391    /// Bounded exchange statements excluded from the interpreted graph.
392    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    /// HEADER records admitted with the cached schema and implementation level.
427    pub(crate) fn header(&self) -> &[HeaderRecord] {
428        &self.header
429    }
430
431    /// Resolved ANCHOR bindings in source order.
432    pub(crate) fn anchors(&self) -> &[AnchorEntry] {
433        &self.anchors
434    }
435
436    /// Admitted external occurrence bindings in source order.
437    pub(crate) fn references(&self) -> &[ReferenceEntry] {
438        &self.references
439    }
440
441    /// Admitted DATA sections and their record populations.
442    pub(crate) fn data(&self) -> &[DataSection] {
443        &self.data
444    }
445
446    /// Validated SIGNATURE extents in source order.
447    pub(crate) fn signatures(&self) -> &[Range<usize>] {
448        &self.signatures
449    }
450
451    /// Shared record graph; mutation would invalidate the cached entity index.
452    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    /// Header-admitted `FILE_SCHEMA` identifiers in source order.
461    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    /// Numeric object-identifier components for the primary schema identifier.
472    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    /// Verbatim `FILE_DESCRIPTION` implementation-level declaration.
479    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    /// Release the source graph and transfer its signature extents for retention.
491    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    // The record graph is immutable while its name index exists.
504    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/// Structural or lexical exchange failure.
552#[derive(Debug, thiserror::Error)]
553pub(crate) enum ParseError {
554    /// Tokenization failed.
555    #[error(transparent)]
556    Lex(#[from] LexError),
557    /// The caller's decode session refused the parse.
558    #[error(transparent)]
559    Resource(#[from] CodecError),
560    /// Token sequence violates the exchange grammar.
561    #[error("{message} at byte {offset}")]
562    Syntax {
563        /// Byte offset of the unexpected token or end of input.
564        offset: usize,
565        /// Violated grammar invariant.
566        message: String,
567    },
568}
569
570/// A recoverable deviation from canonical Part 21 source syntax.
571#[derive(Debug, Clone, Copy, PartialEq, Eq)]
572pub(crate) enum ParseDiagnosticKind {
573    /// A bounded instance or a dependent instance remains source-only.
574    RecordOmitted,
575    /// A bounded noncanonical prefix remains source-only.
576    PreambleOmitted,
577    /// Draft exchange records use the Part 21 instance grammar.
578    DraftExchangeGrammar,
579    /// EOF closes readable records without a complete exchange terminator.
580    EnvelopeIncomplete,
581    /// Descriptive header metadata is nonconforming but readable.
582    HeaderMetadataNoncanonical,
583    /// A bounded presentation value remains source-only.
584    PresentationMetadataUnusable,
585    /// Complex-entity partials are not in their canonical alphabetical order.
586    ComplexPartialsNotAlphabetical,
587    /// A simple named carrier omits its inherited `name` value.
588    OmittedEntityName,
589    /// A known name slot has a present non-string value.
590    EntityNameUnreadable,
591    /// A `FILE_SCHEMA` object identifier has a component outside the range
592    /// that its position permits.
593    SchemaObjectIdentifierOutOfRange,
594    /// `FILE_DESCRIPTION` declares an implementation level whose grammar is
595    /// not implemented; parsing continued with the edition-3 class-3 grammar.
596    ImplementationLevelUnverified,
597    /// An anchor or REFERENCE expansion passed its value-node limit; the
598    /// binding or instance remains source-only.
599    ExpansionBounded,
600}
601
602/// One attributable parser diagnostic that does not prevent recovery.
603#[derive(Debug, Clone, PartialEq, Eq)]
604pub(crate) struct ParseDiagnostic {
605    /// Byte offset of the containing source record.
606    pub(crate) offset: usize,
607    /// Stable diagnostic classification.
608    pub(crate) kind: ParseDiagnosticKind,
609    /// Human-readable explanation, including the observed and canonical order.
610    pub(crate) message: String,
611}
612
613/// Parse one exchange structure in the caller's decode session.
614pub(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
692/// External-mapping parameter counts for owned simple named carriers.
693///
694/// The list is limited to carriers handled by the STEP reader. Context,
695/// representation-map, relationship, and shape-definition entities have
696/// different first attributes and must keep their positional layout.
697/// A present malformed name keeps its slot; only a shorter mapping can omit it.
698fn 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                    &parameters,
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        // Validate the source occurrence class before local REFERENCES can
1151        // replace a forbidden token with an ordinary value.
1152        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(&parameters.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(&parameters, 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                // An omitted name can expose the coordinate list in this slot.
1476                // Its contents retain required numeric admission.
1477                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                    // discarded-value: consume balanced metadata values; the raw record owns their exact bytes.
1539                    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
1637/// Validate the three required header records, and admit the `FILE_SCHEMA`
1638/// identifier list.
1639enum 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
1675/// Admit interpretation fields independently of header metadata conformance.
1676fn 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    // Producer metadata after the author and organization lists may be unset.
1825    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
1869/// One diagnostic for each `FILE_SCHEMA` identifier that the header admits
1870/// under its schema name alone.
1871fn 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
2375/// The admitted `FILE_SCHEMA` identifiers, for schema-name matching.
2376fn 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    /// A value-node or payload limit stopped the expansion; the message
2427    /// names the bound.
2428    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/// Anchors and instances whose expansion passed a value-node limit, each with
2444/// the bound that stopped it.
2445#[derive(Default)]
2446struct BoundedExpansions {
2447    anchors: BTreeMap<String, String>,
2448    records: BTreeMap<u64, String>,
2449}
2450
2451/// Anchors and instances that a value-node limit removed from the
2452/// interpreted graph.
2453#[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
2477/// What an item's expansion is known to do before it copies anything.
2478enum KnownOutcome {
2479    /// Every expansion in the item fits the remaining nodes.
2480    Fits,
2481    /// The item stops at this bound.
2482    Bounded(String),
2483    /// Only a full expansion decides the item.
2484    Unknown,
2485}
2486
2487/// The bounds that one top-level expansion met before. An expansion that
2488/// starts from an empty reference stack checks the same bounds in the same
2489/// order at every budget, so a node-limit bound recurs at every smaller
2490/// budget and any other bound, met before the node limit, recurs at every
2491/// larger budget.
2492#[derive(Default)]
2493struct ExpansionBounds {
2494    /// The largest budget at which the expansion passed the node limit.
2495    node_limit_at: Option<usize>,
2496    /// The smallest budget at which the expansion met another bound, with
2497    /// that bound.
2498    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    /// Each expanded anchor with its value nodes and payload octets.
2524    memo: BTreeMap<&'a str, (Value, usize, u64)>,
2525    bounds: BTreeMap<&'a str, ExpansionBounds>,
2526    remaining_nodes: usize,
2527    /// Value nodes that omitted items may still copy; `None` once spent.
2528    discard_allowance: Option<usize>,
2529    /// Value nodes that the current item has copied or counted.
2530    item_nodes: usize,
2531    payload: PayloadAllowance,
2532    budget: &'ctx DecodeContext<'arena>,
2533}
2534
2535impl<'a, 'ctx, 'arena> AnchorResolver<'a, 'ctx, 'arena> {
2536    /// Value nodes that anchor expansion may copy across the whole exchange.
2537    const MAX_EXPANDED_NODES: usize = 1_000_000;
2538    /// Value nodes that items omitted at the node limit may copy across the
2539    /// whole exchange before their expansion stopped.
2540    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    /// Resolves every value of one anchor or instance. A value that passes
2560    /// the node limit leaves the item unresolved, returns the bound, and
2561    /// gives back the nodes that the item's earlier values used.
2562    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    /// Decides the item from earlier expansions of its top-level
2593    /// references, without copying.
2594    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    /// Expands anchor `name`, which is not memoized and not on `stack`.
2769    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    /// Value nodes and payload octets that each top-level occurrence used
2804    /// when it expanded.
2805    used: BTreeMap<ReferenceName, (usize, u64)>,
2806    bounds: BTreeMap<ReferenceName, ExpansionBounds>,
2807    remaining_nodes: usize,
2808    /// Value nodes that omitted items may still copy; `None` once spent.
2809    discard_allowance: Option<usize>,
2810    /// Value nodes that the current item has copied.
2811    item_nodes: usize,
2812    payload: PayloadAllowance,
2813    budget: &'ctx DecodeContext<'arena>,
2814}
2815
2816impl<'a, 'ctx, 'arena> ReferenceResolver<'a, 'ctx, 'arena> {
2817    /// Value nodes that REFERENCE expansion may copy across the whole
2818    /// exchange.
2819    const MAX_MATERIALIZED_NODES: usize = 1_000_000;
2820    /// Value nodes that items omitted at the node limit may copy across the
2821    /// whole exchange before their expansion stopped.
2822    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    /// Resolves every value of one anchor or instance. A value that passes
2873    /// the node limit leaves the item unresolved, returns the bound, and
2874    /// gives back the nodes that the item's earlier values used.
2875    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    /// Decides the item from earlier expansions of its top-level
2906    /// occurrences, without copying.
2907    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    /// Charges the node and payload octets that copying `value`'s own node
3070    /// out of an anchor costs.
3071    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
3087/// Resolves local REFERENCE occurrences and returns the anchors and instances
3088/// whose expansion passed the value-node or payload limit.
3089fn 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
3153/// Count the value nodes of `value` before anchor expansion copies it. A
3154/// value larger than `limit` nodes is [`ResolveError::Bounded`].
3155fn 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;