ifc_lite_landxml/alignment/
parser.rs1mod actions;
8mod driver;
9mod state;
10
11use std::collections::HashMap;
12
13use quick_xml::{events::Event, Reader};
14
15use super::{LandXmlAlignment, LandXmlAlignmentDocument, LandXmlSuperelevationEventKind};
16use crate::{
17 preflight::preflight_xml_tokens,
18 xml::{error, normalize_encoding, Result},
19 LandXmlCancellation, LandXmlCapabilityDiagnostic, LandXmlDiagnosticCode as Code, LandXmlLimits,
20 LandXmlUnits,
21};
22
23#[derive(Clone, Debug)]
25pub struct LandXmlAlignmentLimits {
26 pub xml: LandXmlLimits,
27 pub max_alignments: usize,
28 pub max_alignment_segments: usize,
29 pub max_alignment_points: usize,
30 pub max_station_equations: usize,
31 pub max_cant_stations: usize,
32 pub max_superelevation_events: usize,
33}
34impl Default for LandXmlAlignmentLimits {
35 fn default() -> Self {
36 Self {
37 xml: LandXmlLimits::default(),
38 max_alignments: 100_000,
39 max_alignment_segments: 5_000_000,
40 max_alignment_points: 5_000_000,
41 max_station_equations: 1_000_000,
42 max_cant_stations: 5_000_000,
43 max_superelevation_events: 5_000_000,
44 }
45 }
46}
47pub fn parse_landxml_alignments(input: &[u8]) -> Result<LandXmlAlignmentDocument> {
49 parse_landxml_alignments_with_cancel(input, &LandXmlAlignmentLimits::default(), None)
50}
51pub fn parse_landxml_alignments_optional(input: &[u8]) -> Result<LandXmlAlignmentDocument> {
56 parse_landxml_alignments_inner(input, &LandXmlAlignmentLimits::default(), None, false)
57}
58pub fn parse_landxml_alignments_optional_with_cancel(
61 input: &[u8],
62 limits: &LandXmlAlignmentLimits,
63 cancelled: Option<&dyn LandXmlCancellation>,
64) -> Result<LandXmlAlignmentDocument> {
65 parse_landxml_alignments_inner(input, limits, cancelled, false)
66}
67pub fn parse_landxml_alignments_with_cancel(
69 input: &[u8],
70 limits: &LandXmlAlignmentLimits,
71 cancelled: Option<&dyn LandXmlCancellation>,
72) -> Result<LandXmlAlignmentDocument> {
73 parse_landxml_alignments_inner(input, limits, cancelled, true)
74}
75fn parse_landxml_alignments_inner(
76 input: &[u8],
77 limits: &LandXmlAlignmentLimits,
78 cancelled: Option<&dyn LandXmlCancellation>,
79 require_alignment: bool,
80) -> Result<LandXmlAlignmentDocument> {
81 if input.len() > limits.xml.max_bytes {
82 return Err(error(Code::InputTooLarge, "input exceeds byte limit"));
83 }
84 let input = normalize_encoding(input, &limits.xml, cancelled)?;
85 preflight_xml_tokens(&input, &limits.xml, cancelled)?;
86 let mut parser = Parser::new(limits, cancelled, require_alignment);
87 let mut reader = Reader::from_reader(input.as_slice());
88 reader.config_mut().trim_text(false);
89 let mut buffer = Vec::new();
90 loop {
91 parser.check(1)?;
92 let event = reader
93 .read_event_into(&mut buffer)
94 .map_err(|_| error(Code::InvalidXml, "malformed XML"))?;
95 if matches!(event, Event::Eof) {
96 break;
97 }
98 parser.consume_event(event)?;
99 buffer.clear();
100 }
101 if !parser.frames.is_empty() {
102 return Err(error(Code::InvalidXml, "unclosed XML element"));
103 }
104 parser.finish()
105}
106#[derive(Clone)]
107pub(super) struct Frame {
108 pub(super) local: String,
109 pub(super) target: bool,
110 pub(super) namespaces: HashMap<String, String>,
111}
112pub(super) enum Capture {
113 Point {
114 local: String,
115 depth: usize,
116 pnt_ref: Option<String>,
117 text: String,
118 },
119 PointList {
120 dimension: usize,
121 depth: usize,
122 text: String,
123 },
124 Superelevation {
125 kind: LandXmlSuperelevationEventKind,
126 depth: usize,
127 text: String,
128 },
129}
130impl Capture {
131 pub(super) fn depth(&self) -> usize {
132 match self {
133 Self::Point { depth, .. }
134 | Self::PointList { depth, .. }
135 | Self::Superelevation { depth, .. } => *depth,
136 }
137 }
138}
139pub(crate) struct Parser<'a> {
140 pub(super) limits: LandXmlAlignmentLimits,
141 pub(super) cancelled: Option<&'a dyn LandXmlCancellation>,
142 pub(super) work: usize,
143 pub(super) character_references: usize,
144 pub(super) frames: Vec<Frame>,
145 pub(super) units: Option<LandXmlUnits>,
146 pub(super) alignments: Vec<LandXmlAlignment>,
147 pub(super) segments_seen: usize,
148 pub(super) alignment_points_seen: usize,
149 pub(super) station_equations_seen: usize,
150 pub(super) cant_stations_seen: usize,
151 pub(super) superelevation_events_seen: usize,
152 pub(super) alignment: Option<state::AlignmentBuilder>,
153 pub(super) capture: Option<Capture>,
154 pub(super) warnings: Vec<String>,
155 pub(super) schema: String,
156 pub(super) version: String,
157 pub(super) capability_diagnostics: Vec<LandXmlCapabilityDiagnostic>,
158 pub(super) target_namespace: Option<String>,
159 pub(super) root_seen: bool,
160 pub(super) root_closed: bool,
161 pub(super) require_alignment: bool,
162}
163impl<'a> Parser<'a> {
164 fn new(
165 limits: &LandXmlAlignmentLimits,
166 cancelled: Option<&'a dyn LandXmlCancellation>,
167 require_alignment: bool,
168 ) -> Self {
169 Self {
170 limits: limits.clone(),
171 cancelled,
172 work: 0,
173 character_references: 0,
174 frames: Vec::new(),
175 units: None,
176 alignments: Vec::new(),
177 segments_seen: 0,
178 alignment_points_seen: 0,
179 station_equations_seen: 0,
180 cant_stations_seen: 0,
181 superelevation_events_seen: 0,
182 alignment: None,
183 capture: None,
184 warnings: Vec::new(),
185 schema: String::new(),
186 version: String::new(),
187 capability_diagnostics: Vec::new(),
188 target_namespace: None,
189 root_seen: false,
190 root_closed: false,
191 require_alignment,
192 }
193 }
194
195 pub(crate) fn consume_event(&mut self, event: Event<'_>) -> Result<()> {
196 match event {
197 Event::Start(value) => self.start(&value),
198 Event::Empty(value) => {
199 self.start(&value)?;
200 self.end(None)
201 }
202 Event::End(value) => self.end(Some(value.name().as_ref().as_bytes())),
203 Event::Text(value) => self.text(value.as_ref().as_bytes()),
204 Event::CData(value) => self.cdata(value.as_ref().as_bytes()),
205 Event::DocType(_) => Err(error(Code::DtdForbidden, "DOCTYPE is not allowed")),
206 _ => Ok(()),
207 }
208 }
209
210 pub(crate) fn has_open_frames(&self) -> bool {
211 !self.frames.is_empty()
212 }
213}
214
215impl Parser<'static> {
216 pub(crate) fn new_stream(limits: LandXmlAlignmentLimits) -> Self {
217 Self::new(&limits, None, false)
218 }
219}