1use super::AutosarDataError;
2use memchr::memchr;
3use std::path::PathBuf;
4use thiserror::Error;
5
6#[derive(Debug, Error, Eq, PartialEq, Clone, Copy)]
7#[non_exhaustive]
8pub enum ArxmlLexerError {
10 #[error("Incomplete data, closing '>' was not found")]
12 IncompleteData,
13
14 #[error("Invalid element: '<>'")]
16 InvalidElement,
17
18 #[error("A processing instruction was started with '<?', but it did not end with '?>'")]
20 InvalidProcessingInstruction,
21
22 #[error("Invalid arxml header: The xml header of an arxml file must specify version=\"1.0\" encoding=\"utf-8\"")]
24 InvalidXmlHeader,
25
26 #[error("Invalid comment")]
28 InvalidComment,
29}
30
31#[derive(Debug)]
32pub(crate) enum ArxmlEvent<'a> {
33 ArxmlHeader(Option<bool>),
34 BeginElement(&'a [u8], &'a [u8]),
35 EndElement(&'a [u8]),
36 Characters(&'a [u8]),
37 Comment(&'a [u8]),
38 EndOfFile,
39}
40
41pub(crate) struct ArxmlLexer<'a> {
42 buffer: &'a [u8],
43 bufpos: usize,
44 line: usize,
45 deferred_end: Option<(usize, usize)>,
46 sourcefile: PathBuf,
47}
48
49impl<'a> ArxmlLexer<'a> {
50 pub(crate) fn new(buffer: &'a [u8], name: PathBuf) -> Self {
51 let bufpos = if buffer.len() > 3 && buffer[0] == 239 && buffer[1] == 187 && buffer[2] == 191 {
53 3
54 } else {
55 0
56 };
57 Self {
58 buffer,
59 bufpos,
60 line: 1,
61 deferred_end: None,
62 sourcefile: name,
63 }
64 }
65
66 fn read_characters(&mut self) -> (ArxmlEvent<'a>, bool) {
67 debug_assert!(self.bufpos < self.buffer.len());
68
69 let mut endpos = self.bufpos;
71 let mut all_whitespace = true;
72 while endpos < self.buffer.len() && self.buffer[endpos] != b'<' {
73 if !self.buffer[endpos].is_ascii_whitespace() {
75 all_whitespace = false;
76 } else if self.buffer[endpos] == b'\n' {
77 self.line += 1;
78 }
79 endpos += 1;
80 }
81 debug_assert!(endpos > self.bufpos);
82
83 let text = &self.buffer[self.bufpos..endpos];
84 self.bufpos = endpos;
85 (ArxmlEvent::Characters(text), all_whitespace)
86 }
87
88 fn read_element_start(&mut self, mut endpos: usize) -> ArxmlEvent<'a> {
89 debug_assert!(self.bufpos < self.buffer.len());
90 debug_assert!(endpos > self.bufpos + 1);
91 debug_assert!(self.buffer[self.bufpos] == b'<');
92
93 let (text, elemname, attributes, is_end) = loop {
96 let (text, is_end) = if self.buffer[endpos - 1] == b'/' {
97 (&self.buffer[self.bufpos + 1..endpos - 1], true)
98 } else {
99 (&self.buffer[self.bufpos + 1..endpos], false)
100 };
101
102 let (elemname, attributes) = if let Some(splitpos) = text.iter().position(u8::is_ascii_whitespace) {
103 (&text[..splitpos], &text[splitpos + 1..])
104 } else {
105 (text, &text[0..0])
106 };
107
108 let Some(quotechar) = unterminated_quote(attributes) else {
112 break (text, elemname, attributes, is_end);
113 };
114 let Some(real_endpos) = self.find_quoted_element_end(endpos, quotechar) else {
115 break (text, elemname, attributes, is_end);
117 };
118 endpos = real_endpos;
119 };
120
121 if is_end {
123 self.deferred_end = Some((self.bufpos + 1, self.bufpos + 1 + elemname.len()));
125 }
126
127 self.line += count_lines(text);
128 self.bufpos = endpos + 1;
129 ArxmlEvent::BeginElement(elemname, attributes)
130 }
131
132 fn find_quoted_element_end(&self, startpos: usize, quotechar: u8) -> Option<usize> {
137 let mut quotechar = Some(quotechar);
138 for (idx, c) in self.buffer[startpos..].iter().enumerate() {
139 if *c == b'<' {
140 return None;
144 } else if let Some(qc) = quotechar {
145 if *c == qc {
146 quotechar = None;
147 }
148 } else if *c == b'>' {
149 return Some(startpos + idx);
150 } else if *c == b'"' || *c == b'\'' {
151 quotechar = Some(*c);
152 }
153 }
154 None
155 }
156
157 fn read_element_end(&mut self, endpos: usize) -> ArxmlEvent<'a> {
158 debug_assert!(self.bufpos < self.buffer.len());
159 debug_assert!(endpos > self.bufpos + 1);
160 debug_assert!(self.buffer[self.bufpos] == b'<');
161
162 let text = &self.buffer[self.bufpos + 2..endpos].trim_ascii_end();
163 self.bufpos = endpos + 1;
164
165 ArxmlEvent::EndElement(text)
166 }
167
168 fn read_xml_header(&mut self, endpos: usize) -> Option<Result<ArxmlEvent<'a>, AutosarDataError>> {
169 debug_assert!(self.bufpos < self.buffer.len());
170 debug_assert!(endpos > self.bufpos + 1);
171 debug_assert!(self.buffer[self.bufpos] == b'<');
172
173 if self.buffer[endpos - 1] != b'?' {
174 return Some(Err(self.error(ArxmlLexerError::InvalidProcessingInstruction)));
175 }
176
177 let text = &self.buffer[self.bufpos + 2..endpos - 1];
178 self.bufpos = endpos + 1;
179
180 let text_trimmed = text.trim_ascii();
181 let (elemname, mut rest) = if let Some(ws_pos) = text_trimmed.iter().position(|c| c.is_ascii_whitespace()) {
182 (&text_trimmed[..ws_pos], &text_trimmed[ws_pos..])
183 } else {
184 (text_trimmed, &text_trimmed[text_trimmed.len()..])
185 };
186
187 let result = if elemname == b"xml" {
188 let mut ver = &text[0..0];
189 let mut encoding = &text[0..0];
190 let mut standalone: Option<bool> = None;
191
192 let valid = loop {
193 rest = rest.trim_ascii_start();
194 if rest.is_empty() {
195 break true;
196 }
197
198 let Some(eq_pos) = rest.iter().position(|c| *c == b'=') else {
199 break false;
200 };
201
202 let attr_name = rest[..eq_pos].trim_ascii_end();
203 if attr_name.is_empty() || attr_name.iter().any(|c| c.is_ascii_whitespace()) {
204 break false;
205 }
206
207 rest = rest[eq_pos + 1..].trim_ascii_start();
208 if rest.is_empty() || (rest[0] != b'"' && rest[0] != b'\'') {
209 break false;
210 }
211
212 let quote = rest[0];
213 rest = &rest[1..];
214 let Some(end_quote_pos) = rest.iter().position(|c| *c == quote) else {
215 break false;
216 };
217
218 let attr_val = &rest[..end_quote_pos];
219 rest = &rest[end_quote_pos + 1..];
220
221 if attr_name == b"version" {
222 ver = attr_val;
223 } else if attr_name == b"encoding" {
224 encoding = attr_val;
225 } else if attr_name == b"standalone" {
226 standalone = Some(attr_val == b"yes");
227 }
228 };
229
230 if !valid
231 || ver != b"1.0"
232 || (encoding != b"utf-8" && encoding != b"UTF-8" && encoding != b"utf8" && encoding != b"UTF8")
233 {
234 Some(Err(self.error(ArxmlLexerError::InvalidXmlHeader)))
235 } else {
236 Some(Ok(ArxmlEvent::ArxmlHeader(standalone)))
237 }
238 } else {
239 None
240 };
241
242 self.line += count_lines(text);
243 result
244 }
245
246 fn read_comment(&mut self, endpos: usize) -> Result<ArxmlEvent<'a>, AutosarDataError> {
247 debug_assert!(self.bufpos < self.buffer.len());
248 debug_assert!(endpos > self.bufpos + 1);
249
250 let startpos = self.bufpos;
251 let text = &self.buffer[startpos..endpos];
252 self.bufpos = endpos + 1;
253
254 if text.len() < 6 || !text.starts_with(b"<!--") || !text.ends_with(b"--") {
255 return Err(AutosarDataError::LexerError {
256 filename: self.sourcefile.clone(),
257 line: self.line,
258 source: ArxmlLexerError::InvalidComment,
259 });
260 }
261 self.line += count_lines(text);
262 let comment = &self.buffer[startpos + 4..endpos - 2];
263 Ok(ArxmlEvent::Comment(comment))
264 }
265}
266
267impl ArxmlLexer<'_> {
268 pub(crate) fn next<'a>(&'a mut self) -> Result<(usize, ArxmlEvent<'a>), AutosarDataError> {
269 if let Some((startpos, endpos)) = self.deferred_end {
271 self.deferred_end = None;
272 Ok((self.line, ArxmlEvent::EndElement(&self.buffer[startpos..endpos])))
273 } else {
274 loop {
275 if self.bufpos == self.buffer.len() {
276 break Ok((self.line, ArxmlEvent::EndOfFile));
277 } else if self.buffer[self.bufpos] == b'<' {
278 let findpos = memchr(b'>', &self.buffer[self.bufpos + 1..])
281 .ok_or_else(|| self.error(ArxmlLexerError::IncompleteData))?;
282 let endpos = self.bufpos + findpos + 1;
285
286 if endpos == self.bufpos + 1 {
287 return Err(self.error(ArxmlLexerError::InvalidElement));
289 }
290
291 match self.buffer[self.bufpos + 1] {
293 b'/' => {
294 return Ok((self.line, self.read_element_end(endpos)));
296 }
297 b'?' => {
298 if let Some(result) = self.read_xml_header(endpos) {
301 let value = result?;
302 return Ok((self.line, value));
303 }
304 }
305 b'!' => {
306 let mut comment_endpos = endpos;
312 while comment_endpos < self.buffer.len()
313 && !self.buffer[comment_endpos - 2..].starts_with(b"-->")
314 {
315 comment_endpos += 1;
316 }
317 if comment_endpos < self.buffer.len() {
318 return self.read_comment(comment_endpos).map(|res| (self.line, res));
319 } else {
320 return Err(self.error(ArxmlLexerError::InvalidComment));
322 }
323 }
324 _ => {
325 return Ok((self.line, self.read_element_start(endpos)));
327 }
328 }
329 } else {
330 if let (ArxmlEvent::Characters(text), false) = self.read_characters() {
332 return Ok((self.line, ArxmlEvent::Characters(text)));
334 }
335 }
336 }
340 }
341 }
342
343 fn error(&self, err: ArxmlLexerError) -> AutosarDataError {
344 AutosarDataError::LexerError {
345 filename: self.sourcefile.clone(),
346 line: self.line,
347 source: err,
348 }
349 }
350}
351
352fn unterminated_quote(text: &[u8]) -> Option<u8> {
356 let mut quotechar = None;
357 for c in text {
358 if let Some(qc) = quotechar {
359 if *c == qc {
360 quotechar = None;
361 }
362 } else if *c == b'"' || *c == b'\'' {
363 quotechar = Some(*c);
364 }
365 }
366 quotechar
367}
368
369fn count_lines(text: &[u8]) -> usize {
370 text.iter().filter(|c| **c == b'\n').count()
371}
372
373#[cfg(test)]
374mod test {
375 use super::*;
376
377 #[test]
378 fn test_basic_functionality() {
379 let data =
380 b"<?xml version=\"1.0\" encoding=\"utf-8\"?><element attr=\"gggg\" attr3>contained characters</element>";
381 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
382 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::ArxmlHeader(None)))));
383 assert!(
384 matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, attrs))) if elem == b"element" && attrs.len() == 17)
385 );
386 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::Characters(text))) if text == b"contained characters"));
387 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::EndElement(elem))) if elem == b"element"));
388 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::EndOfFile))));
389 }
390
391 #[test]
392 fn skip_byte_order_mark() {
393 let data =
394 b"\xEF\xBB\xBF<?xml version=\"1.0\" encoding=\"utf-8\"?><element attr=\"gggg\" attr3>contained characters</element>";
395 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
396 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::ArxmlHeader(None)))));
397 }
398
399 #[test]
400 fn test_incomplete_data() {
401 let data = b"<element";
402 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
403 assert!(
404 matches!(lexer.next(), Err(AutosarDataError::LexerError {source, ..}) if source == ArxmlLexerError::IncompleteData)
405 );
406 }
407
408 #[test]
409 fn test_invalid_element() {
410 let data = b"<element><>";
411 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
412 assert!(lexer.next().is_ok());
413 assert!(
414 matches!(lexer.next(), Err(AutosarDataError::LexerError{source, ..}) if source == ArxmlLexerError::InvalidElement)
415 );
416 }
417
418 #[test]
419 fn test_invalid_processing_instruction() {
420 let data = b"<element><?what>";
421 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
422 assert!(lexer.next().is_ok());
423 assert!(
424 matches!(lexer.next(), Err(AutosarDataError::LexerError{source, ..}) if source == ArxmlLexerError::InvalidProcessingInstruction)
425 );
426 }
427
428 #[test]
429 fn test_comment() {
430 let data = b"<!-- foo--><element>";
431 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
432 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::Comment(_)))));
433 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(_elem, _attrs)))));
434 }
435
436 #[test]
437 fn test_invalid_comment() {
438 let data = b"<element><!-- foo>";
439 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
440 assert!(lexer.next().is_ok());
441 assert!(
442 matches!(lexer.next(), Err(AutosarDataError::LexerError{source, ..}) if source == ArxmlLexerError::InvalidComment)
443 );
444 }
445
446 #[test]
447 fn test_invalid_xml_header() {
448 let data = br#"<?xml version="1.0" encoding="cp1252"?>"#;
449 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
450 assert!(
451 matches!(lexer.next(), Err(AutosarDataError::LexerError{source, ..}) if source == ArxmlLexerError::InvalidXmlHeader)
452 );
453
454 let data = br#"<?xml ?>"#;
455 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
456 assert!(
457 matches!(lexer.next(), Err(AutosarDataError::LexerError{source, ..}) if source == ArxmlLexerError::InvalidXmlHeader)
458 );
459 }
460
461 #[test]
462 fn test_malformed_xml_header_attributes() {
463 let malformed: &[&[u8]] = &[
465 br#"<?xml version="1.0" encoding="utf-8" standalone?>"#,
467 br#"<?xml ="1.0"?>"#,
469 br#"<?xml version number="1.0"?>"#,
471 br#"<?xml version=1.0?>"#,
473 br#"<?xml version="1.0?>"#,
475 ];
476 for data in malformed {
477 println!("checking: {}", String::from_utf8_lossy(data));
478 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
479 assert!(
480 matches!(lexer.next(), Err(AutosarDataError::LexerError{source, ..}) if source == ArxmlLexerError::InvalidXmlHeader)
481 );
482 }
483 }
484
485 #[test]
486 fn test_xml_header_with_single_quotes_and_standalone() {
487 let data = br#"<?xml version='1.0' encoding='UTF8' standalone='yes'?><element>"#;
488 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
489 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::ArxmlHeader(Some(true))))));
490 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, _))) if elem == b"element"));
491 }
492
493 #[test]
494 fn test_processing_instruction_is_ignored() {
495 let data = b"<?php echo 'hello'; ?><element>";
498 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
499 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, _))) if elem == b"element"));
500 }
501
502 #[test]
503 fn test_malformed_comment() {
504 let data = b"<!DOCTYPE something -->";
506 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
507 assert!(
508 matches!(lexer.next(), Err(AutosarDataError::LexerError{source, ..}) if source == ArxmlLexerError::InvalidComment)
509 );
510
511 let data = b"<!-->";
513 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
514 assert!(
515 matches!(lexer.next(), Err(AutosarDataError::LexerError{source, ..}) if source == ArxmlLexerError::InvalidComment)
516 );
517 }
518
519 #[test]
520 fn traits() {
521 let err = ArxmlLexerError::IncompleteData;
523 let err2 = err;
524 assert_eq!(err, err2);
525 assert_eq!(format!("{err:#?}"), format!("{err2:#?}"));
526 assert_eq!(format!("{err}"), format!("{err2}"));
527
528 let event = ArxmlEvent::ArxmlHeader(None);
530 let _ = format!("{event:#?}");
531 }
532
533 #[test]
535 fn test_w3c_comment_example() {
536 let data = b"<!-- declarations for <head> & <body> -->";
537 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
538 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::Comment(_)))));
539 }
540
541 #[test]
543 fn test_gt_in_attribute_value() {
544 let data = b"<element attr=\"a>b\">text</element>";
546 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
547 assert!(
548 matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, attrs))) if elem == b"element" && attrs == b"attr=\"a>b\"")
549 );
550 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::Characters(text))) if text == b"text"));
551 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::EndElement(elem))) if elem == b"element"));
552
553 let data = b"<element attr='a>\"b'>";
555 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
556 assert!(
557 matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, attrs))) if elem == b"element" && attrs == b"attr='a>\"b'")
558 );
559
560 let data = b"<element a=\"1\" b='2' c=\"3>4\" d=\"5\">";
562 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
563 assert!(
564 matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, attrs))) if elem == b"element" && attrs == b"a=\"1\" b='2' c=\"3>4\" d=\"5\"")
565 );
566
567 let data = b"<element attr=\"a/>b\"/>";
569 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
570 assert!(
571 matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, attrs))) if elem == b"element" && attrs == b"attr=\"a/>b\"")
572 );
573 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::EndElement(elem))) if elem == b"element"));
574 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::EndOfFile))));
575
576 let data = b"<element attr=\"a>\nb\"><x>";
578 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
579 assert!(matches!(lexer.next(), Ok((1, ArxmlEvent::BeginElement(_, _)))));
580 assert!(matches!(lexer.next(), Ok((2, ArxmlEvent::BeginElement(elem, _))) if elem == b"x"));
581 }
582
583 #[test]
586 fn test_unterminated_attribute_value() {
587 let data = b"<element attr=\"abc>text</element>";
589 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
590 assert!(
591 matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, attrs))) if elem == b"element" && attrs == b"attr=\"abc")
592 );
593 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::Characters(text))) if text == b"text"));
594 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::EndElement(elem))) if elem == b"element"));
595
596 let data = b"<element attr=\"abc>text";
598 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
599 assert!(
600 matches!(lexer.next(), Ok((_, ArxmlEvent::BeginElement(elem, attrs))) if elem == b"element" && attrs == b"attr=\"abc")
601 );
602 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::Characters(text))) if text == b"text"));
603 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::EndOfFile))));
604 }
605
606 #[test]
608 fn test_xml_header_with_extra_spaces() {
609 let data = b"<?xml version = \"1.0\" encoding = \"utf-8\" standalone = \"yes\" ?>";
610 let mut lexer = ArxmlLexer::new(data, PathBuf::from("(buffer)"));
611 assert!(matches!(lexer.next(), Ok((_, ArxmlEvent::ArxmlHeader(Some(true))))));
612 }
613}