1use std::ops::Range;
5
6use cadmpeg_core::decode::DecodeContext;
7use cadmpeg_core::CodecError;
8use cadmpeg_ir::scalar::FiniteReal;
9
10#[derive(Debug, Clone, Copy)]
12enum OccurrencePrefix {
13 Entity,
14 Value,
15}
16
17#[derive(Debug, Clone, PartialEq)]
19pub(crate) struct Token {
20 pub(crate) kind: TokenKind,
22 pub(crate) span: Range<usize>,
24}
25
26#[derive(Debug, Clone, PartialEq)]
28pub(crate) enum TokenKind {
29 InvalidSource(String),
31 Name(String),
33 UserName(String),
35 Instance(u64),
37 ValueInstance(u64),
39 ConstantEntity(String),
41 ConstantValue(String),
43 Integer(i64),
45 Real(FiniteReal),
47 UninterpretedLiteral,
49 Enumeration(String),
51 String(Vec<u8>),
53 Binary(BinaryValue),
55 Resource(String),
57 LParen,
59 RParen,
61 Comma,
63 Semicolon,
65 Equals,
67 Omitted,
69 Derived,
71 TagName(String),
73 LBrace,
75 RBrace,
77 Colon,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83pub(crate) struct BinaryValue {
84 unused_bits: u8,
86 data: Box<[u8]>,
87}
88
89impl BinaryValue {
90 pub(crate) fn bit_len(&self) -> usize {
91 self.data.len() * 8 - usize::from(self.unused_bits)
92 }
93
94 pub(crate) fn data(&self) -> &[u8] {
95 &self.data
96 }
97}
98
99#[derive(Debug, thiserror::Error)]
101#[error("{message} at byte {offset}")]
102pub(crate) struct LexError {
103 offset: usize,
105 pub(crate) message: String,
107 resource: Option<CodecError>,
108}
109
110impl LexError {
111 pub(crate) fn offset(&self) -> usize {
112 self.offset
113 }
114 pub(crate) fn into_codec_error(self) -> CodecError {
115 match self.resource {
116 Some(error) => error,
117 None => CodecError::malformed(format_args!("{} at byte {}", self.message, self.offset)),
118 }
119 }
120
121 pub(crate) fn into_resource_error(self) -> Option<CodecError> {
122 self.resource
123 }
124}
125
126#[derive(Clone, Copy)]
127pub(crate) enum LiteralAdmission {
128 Required,
129 Metadata,
130}
131
132#[derive(Clone, Copy)]
133enum ExchangeSyntax {
134 Standard,
135 Draft,
136}
137
138pub(crate) struct Lexer<'a, 'ctx, 'arena> {
139 input: &'a [u8],
140 budget: &'ctx DecodeContext<'arena>,
141 literal_admission: LiteralAdmission,
142 at: usize,
143 allow_print_controls: bool,
144 previous_was_signature: bool,
145 tag_name_expected: bool,
146 syntax: ExchangeSyntax,
147}
148
149pub(crate) fn print_control_end(input: &[u8], at: usize) -> Option<usize> {
150 match_exact_ignoring_controls(input, at, b"\\N\\")
151 .or_else(|| match_exact_ignoring_controls(input, at, b"\\F\\"))
152}
153
154impl<'a, 'ctx, 'arena> Lexer<'a, 'ctx, 'arena> {
155 pub(crate) fn new(input: &'a [u8], ctx: &'ctx DecodeContext<'arena>) -> Self {
156 let draft_offset = crate::codec::draft_exchange_offset(input);
157 Self {
158 input,
159 budget: ctx,
160 literal_admission: LiteralAdmission::Required,
161 at: draft_offset.unwrap_or(0),
162 allow_print_controls: true,
163 previous_was_signature: false,
164 tag_name_expected: false,
165 syntax: if draft_offset.is_some() {
166 ExchangeSyntax::Draft
167 } else {
168 ExchangeSyntax::Standard
169 },
170 }
171 }
172
173 pub(crate) fn set_literal_admission(&mut self, admission: LiteralAdmission) {
174 self.literal_admission = admission;
175 }
176
177 pub(crate) fn allows_uninterpreted_literals(&self) -> bool {
178 matches!(self.literal_admission, LiteralAdmission::Metadata)
179 }
180
181 pub(crate) fn set_allow_print_controls(&mut self, allow: bool) {
182 self.allow_print_controls = allow;
183 }
184
185 pub(crate) fn input(&self) -> &[u8] {
186 self.input
187 }
188
189 pub(crate) fn is_draft(&self) -> bool {
190 matches!(self.syntax, ExchangeSyntax::Draft)
191 }
192
193 pub(crate) fn seek(&mut self, at: usize) {
194 self.at = at;
195 self.tag_name_expected = false;
196 self.previous_was_signature = false;
197 }
198
199 pub(crate) fn next_token(&mut self) -> Result<Option<Token>, LexError> {
200 if !self.skip_trivia()? {
201 return Ok(None);
202 }
203 let token = self.token()?;
204 let skip_signature =
205 self.previous_was_signature && matches!(&token.kind, TokenKind::Semicolon);
206 self.previous_was_signature =
207 matches!(&token.kind, TokenKind::Name(name) if name == "SIGNATURE");
208 if skip_signature {
209 self.skip_signature_payload()?;
210 }
211 Ok(Some(token))
212 }
213
214 fn skip_signature_payload(&mut self) -> Result<(), LexError> {
215 let start = self.at;
216 let name_len = b"ENDSEC".len();
217 let mut at = start;
218 let mut boundary_allowed = true;
219 while at + name_len <= self.input.len() {
220 if self.input.get(at..at + 2) == Some(b"/*") {
221 let comment_start = at;
222 at += 2;
223 let Some(end) = self.input[at..].windows(2).position(|w| w == b"*/") else {
224 return Err(Self::error(comment_start, "unterminated comment"));
225 };
226 at += end + 2;
227 boundary_allowed = true;
228 continue;
229 }
230 if let Some(end) = self.print_control_end(at) {
231 at = end;
232 boundary_allowed = true;
233 continue;
234 }
235 if self
236 .input
237 .get(at)
238 .is_some_and(|byte| byte.is_ascii_control() || *byte == b' ')
239 {
240 at += 1;
241 boundary_allowed = true;
242 continue;
243 }
244 let candidate = at;
245 let Some(mut after_name) = match_ignoring_controls(self.input, candidate, b"ENDSEC")
246 else {
247 at += 1;
248 boundary_allowed = false;
249 continue;
250 };
251 while self
252 .input
253 .get(after_name)
254 .is_some_and(|byte| byte.is_ascii_control() || *byte == b' ')
255 {
256 after_name += 1;
257 }
258 if boundary_allowed && self.input.get(after_name) == Some(&b';') {
259 self.validate_signature_payload(start, candidate)?;
260 self.at = candidate;
261 return Ok(());
262 }
263 at += 1;
264 boundary_allowed = false;
265 }
266 Err(Self::error(start, "unterminated signature section"))
267 }
268
269 fn validate_signature_payload(&self, start: usize, end: usize) -> Result<(), LexError> {
270 let mut quantum_len = 0;
271 let mut padding = 0;
272 let mut finished = false;
273 let mut saw_content = false;
274 let mut trailing_separator = false;
275 let mut relative = 0;
276 while relative < end - start {
277 let byte = self.input[start + relative];
278 if byte.is_ascii_control() {
279 relative += 1;
280 continue;
281 }
282 if byte == b' ' {
283 trailing_separator |= saw_content;
284 relative += 1;
285 continue;
286 }
287 if let Some(separator_end) = self.print_control_end(start + relative) {
288 if separator_end > end {
289 return Err(Self::error(
290 start + relative,
291 "invalid SIGNATURE base64 character",
292 ));
293 }
294 trailing_separator |= saw_content;
295 relative = separator_end - start;
296 continue;
297 }
298 if self.input.get(start + relative..start + relative + 2) == Some(b"/*") {
299 let comment_start = start + relative;
300 let comment_body = comment_start + 2;
301 let Some(comment_end) = self.input[comment_body..end]
302 .windows(2)
303 .position(|window| window == b"*/")
304 else {
305 return Err(Self::error(comment_start, "unterminated comment"));
306 };
307 trailing_separator |= saw_content;
308 relative = comment_body + comment_end + 2 - start;
309 continue;
310 }
311 if trailing_separator {
312 return Err(Self::error(
313 start + relative,
314 "invalid SIGNATURE base64 character",
315 ));
316 }
317 saw_content = true;
318 let is_alphabet = byte.is_ascii_alphanumeric() || matches!(byte, b'+' | b'/');
319 if finished || (padding != 0 && is_alphabet) {
320 return Err(Self::error(
321 start + relative,
322 "invalid SIGNATURE base64 padding",
323 ));
324 }
325 if is_alphabet {
326 quantum_len += 1;
327 } else if byte == b'=' {
328 if quantum_len < 2 || padding == 2 {
329 return Err(Self::error(
330 start + relative,
331 "invalid SIGNATURE base64 padding",
332 ));
333 }
334 padding += 1;
335 quantum_len += 1;
336 } else {
337 return Err(Self::error(
338 start + relative,
339 "invalid SIGNATURE base64 character",
340 ));
341 }
342 if quantum_len == 4 {
343 finished = padding != 0;
344 quantum_len = 0;
345 padding = 0;
346 }
347 relative += 1;
348 }
349 if !saw_content {
350 return Err(Self::error(
351 start,
352 "SIGNATURE section has empty base64 content",
353 ));
354 }
355 if quantum_len != 0 {
356 return Err(Self::error(
357 end,
358 "SIGNATURE base64 content has incomplete quantum",
359 ));
360 }
361 Ok(())
362 }
363
364 fn skip_trivia(&mut self) -> Result<bool, LexError> {
365 loop {
366 while self.input.get(self.at).is_some_and(u8::is_ascii_control) {
367 self.at += 1;
368 }
369 if let Some(end) = self.print_control_end(self.at) {
370 if !self.allow_print_controls {
371 return Err(Self::error(
372 self.at,
373 "print control directive is not allowed in this section",
374 ));
375 }
376 self.at = end;
377 continue;
378 }
379 if self.input.get(self.at) == Some(&b' ') {
380 self.at += 1;
381 continue;
382 }
383 let close = match self.input.get(self.at..self.at + 2) {
384 Some(b"/*") => b"*/",
385 Some(b"!*") if self.is_draft() => b"*!",
386 _ => return Ok(self.at < self.input.len()),
387 };
388 let start = self.at;
389 self.at += 2;
390 let Some(end) = self.input[self.at..].windows(2).position(|w| w == close) else {
391 return Err(Self::error(start, "unterminated comment"));
392 };
393 self.at += end + 2;
394 }
395 }
396
397 fn token(&mut self) -> Result<Token, LexError> {
398 let start = self.at;
399 if self.tag_name_expected {
400 self.tag_name_expected = false;
401 let kind = self.tag_name()?;
402 return Ok(Token {
403 kind,
404 span: start..self.at,
405 });
406 }
407 let byte = self.input[self.at];
408 let kind = match byte {
409 b'(' => self.one(TokenKind::LParen),
410 b')' => self.one(TokenKind::RParen),
411 b',' => self.one(TokenKind::Comma),
412 b';' => self.one(TokenKind::Semicolon),
413 b'=' => self.one(TokenKind::Equals),
414 b'{' => self.one(TokenKind::LBrace),
415 b'}' => self.one(TokenKind::RBrace),
416 b':' => self.one(TokenKind::Colon),
417 b'$' => self.one(TokenKind::Omitted),
418 b'*' => self.one(TokenKind::Derived),
419 b'#' => self.occurrence(OccurrencePrefix::Entity)?,
420 b'@' => self.occurrence(OccurrencePrefix::Value)?,
421 b'\'' => self.string()?,
422 b'"' => self.binary()?,
423 b'<' => self.resource()?,
424 b'.' if self
425 .next_non_ignored(self.at + 1)
426 .is_some_and(|byte| byte.is_ascii_alphabetic() || byte == b'_') =>
427 {
428 self.enumeration()?
429 }
430 b'!' => self.user_name()?,
431 b'+' | b'-' | b'0'..=b'9' | b'.' => self.number()?,
432 b if b.is_ascii_alphabetic() || b == b'_' => TokenKind::Name(self.name()),
433 _ => return Err(Self::error(start, "unexpected byte")),
434 };
435 Ok(Token {
436 kind,
437 span: start..self.at,
438 })
439 }
440
441 fn one(&mut self, kind: TokenKind) -> TokenKind {
442 self.at += 1;
443 if matches!(kind, TokenKind::LBrace) {
444 self.tag_name_expected = true;
445 }
446 kind
447 }
448
449 fn name(&mut self) -> String {
450 let start = self.at;
451 self.at += 1;
452 while self.input.get(self.at).is_some_and(|b| {
453 b.is_ascii_alphanumeric() || *b == b'_' || *b == b'-' || b.is_ascii_control()
454 }) {
455 self.at += 1;
456 }
457 let mut name = self.normalized(start, self.at);
458 name.make_ascii_uppercase();
459 name
460 }
461
462 fn tag_name(&mut self) -> Result<TokenKind, LexError> {
463 let start = self.at;
464 if !self
465 .input
466 .get(self.at)
467 .is_some_and(|byte| byte.is_ascii_alphabetic() || *byte == b'_')
468 {
469 return Err(Self::error(start, "tag name has no identifier"));
470 }
471 self.at += 1;
472 while self.input.get(self.at).is_some_and(|byte| {
473 byte.is_ascii_alphanumeric() || *byte == b'_' || byte.is_ascii_control()
474 }) {
475 self.at += 1;
476 }
477 let name = self.normalized(start, self.at);
478 Ok(TokenKind::TagName(name))
479 }
480
481 fn user_name(&mut self) -> Result<TokenKind, LexError> {
482 let start = self.at;
483 self.at += 1;
484 self.skip_ignored();
485 if !self
486 .input
487 .get(self.at)
488 .is_some_and(|byte| byte.is_ascii_alphabetic() || *byte == b'_')
489 {
490 return Err(Self::error(start, "user-defined name has no identifier"));
491 }
492 Ok(TokenKind::UserName(self.name()))
493 }
494
495 fn occurrence(&mut self, prefix: OccurrencePrefix) -> Result<TokenKind, LexError> {
496 let start = self.at;
497 self.at += 1;
498 self.skip_ignored();
499 match self.input.get(self.at).copied() {
500 Some(byte) if byte.is_ascii_digit() => {
501 let digits = self.at;
502 while let Some(byte) = self.input.get(self.at).copied() {
503 if byte.is_ascii_digit() || byte.is_ascii_control() {
504 self.at += 1;
505 } else {
506 break;
507 }
508 }
509 let raw = self.normalized(digits, self.at);
510 let value = raw
511 .parse::<u64>()
512 .ok()
513 .ok_or_else(|| Self::error(start, "instance name is out of range"))?;
514 if value == 0 {
515 return Err(Self::error(start, "instance name must not be zero"));
516 }
517 match prefix {
518 OccurrencePrefix::Entity => Ok(TokenKind::Instance(value)),
519 OccurrencePrefix::Value => Ok(TokenKind::ValueInstance(value)),
520 }
521 }
522 Some(byte) if byte.is_ascii_alphabetic() || byte == b'_' => {
523 let name_start = self.at;
524 self.at += 1;
525 while let Some(byte) = self.input.get(self.at).copied() {
526 if byte.is_ascii_alphanumeric() || byte == b'_' || byte.is_ascii_control() {
527 self.at += 1;
528 } else {
529 break;
530 }
531 }
532 let mut name = self.normalized(name_start, self.at);
533 name.make_ascii_uppercase();
534 match prefix {
535 OccurrencePrefix::Entity => Ok(TokenKind::ConstantEntity(name)),
536 OccurrencePrefix::Value => Ok(TokenKind::ConstantValue(name)),
537 }
538 }
539 _ => Err(Self::error(start, "occurrence name has no identifier")),
540 }
541 }
542
543 fn number(&mut self) -> Result<TokenKind, LexError> {
544 let start = self.at;
545 if matches!(self.input[self.at], b'+' | b'-') {
546 self.at += 1;
547 self.skip_ignored();
548 }
549 let mut dot = false;
550 let mut exponent = false;
551 while let Some(&b) = self.input.get(self.at) {
552 match b {
553 byte if byte.is_ascii_control() => self.at += 1,
554 b'0'..=b'9' => self.at += 1,
555 b'.' if !dot => {
556 dot = true;
557 self.at += 1;
558 }
559 b'E' | b'e' | b'D' | b'd' if !exponent => {
560 exponent = true;
561 self.at += 1;
562 self.skip_ignored();
563 if self
564 .input
565 .get(self.at)
566 .is_some_and(|b| matches!(b, b'+' | b'-'))
567 {
568 self.at += 1;
569 self.skip_ignored();
570 }
571 }
572 _ => break,
573 }
574 }
575 let mut raw = self.normalized(start, self.at);
576 if exponent && raw.ends_with('.') {
577 raw.pop();
578 }
579 let admitted = if dot || exponent {
580 raw.make_ascii_uppercase();
581 let mut index = 0;
582 while index < raw.len() {
583 if raw.as_bytes()[index] == b'D' {
584 raw.replace_range(index..=index, "E");
585 }
586 index += 1;
587 }
588 raw.parse::<f64>()
589 .map_err(|_| Self::error(start, "invalid real"))
590 .and_then(|value| {
591 FiniteReal::new(value)
592 .map(TokenKind::Real)
593 .ok_or_else(|| Self::error(start, "real exceeds finite binary64 range"))
594 })
595 } else {
596 raw.parse()
597 .map(TokenKind::Integer)
598 .map_err(|_| Self::error(start, "invalid integer"))
599 };
600 match (self.literal_admission, admitted) {
601 (LiteralAdmission::Metadata, Err(_)) => Ok(TokenKind::UninterpretedLiteral),
602 (_, result) => result,
603 }
604 }
605
606 fn enumeration(&mut self) -> Result<TokenKind, LexError> {
607 let start = self.at;
608 self.at += 1;
609 self.skip_ignored();
610 let name_start = self.at;
611 while self
612 .input
613 .get(self.at)
614 .is_some_and(|b| b.is_ascii_alphanumeric() || *b == b'_' || b.is_ascii_control())
615 {
616 self.at += 1;
617 }
618 self.skip_ignored();
619 if self.input.get(self.at) != Some(&b'.') {
620 return Err(Self::error(start, "unterminated enumeration"));
621 }
622 let mut name = self.normalized(name_start, self.at);
623 name.make_ascii_uppercase();
624 self.at += 1;
625 Ok(TokenKind::Enumeration(name))
626 }
627
628 fn string(&mut self) -> Result<TokenKind, LexError> {
629 let start = self.at;
630 self.at += 1;
631 let content = self.at;
632 loop {
633 match self.input.get(self.at).copied() {
634 Some(b'\'') => {
635 if let Some(end) = self.match_exact_ignoring_controls(self.at, b"''") {
636 self.at = end;
637 } else {
638 let mut bytes = self.input[content..self.at].to_vec();
639 bytes.retain(|byte| !byte.is_ascii_control());
640 self.at += 1;
641 return Ok(TokenKind::String(bytes));
642 }
643 }
644 Some(byte) if byte.is_ascii_control() => {
645 self.at += 1;
646 }
647 Some(b'\\') => {
648 if let Some(end) = self.print_control_end(self.at) {
649 if !self.allow_print_controls {
650 return Err(Self::error(
651 self.at,
652 "print control directive is not allowed in this section",
653 ));
654 }
655 self.at = end;
656 } else {
657 self.at += 1;
658 }
659 }
660 Some(_) => self.at += 1,
661 None => return Err(Self::error(start, "unterminated string")),
662 }
663 }
664 }
665
666 fn binary(&mut self) -> Result<TokenKind, LexError> {
667 let start = self.at;
668 self.binary_value()
669 .or_else(|error| self.recover_metadata_literal(start, b'"', error))
670 }
671
672 fn recover_metadata_literal(
673 &mut self,
674 start: usize,
675 closing: u8,
676 error: LexError,
677 ) -> Result<TokenKind, LexError> {
678 if error.resource.is_some() || !matches!(self.literal_admission, LiteralAdmission::Metadata)
679 {
680 return Err(error);
681 }
682 for end in start + 1..self.input.len() {
683 if self.input[end] == closing {
684 self.at = end + 1;
685 return Ok(TokenKind::UninterpretedLiteral);
686 }
687 }
688 Err(error)
689 }
690
691 fn binary_value(&mut self) -> Result<TokenKind, LexError> {
692 let start = self.at;
693 self.at += 1;
694 let content = self.at;
695 let mut digit_count = 0usize;
696 while let Some(byte) = self.input.get(self.at).copied() {
697 if HexDigit::new(byte).is_some() {
698 digit_count += 1;
699 self.at += 1;
700 } else if byte.is_ascii_control() {
701 self.at += 1;
702 } else if byte == b'\\' {
703 let Some(after_print_control) = self.print_control_end(self.at) else {
704 break;
705 };
706 if !self.allow_print_controls {
707 return Err(Self::error(
708 self.at,
709 "print control directive is not allowed in this section",
710 ));
711 }
712 self.at = after_print_control;
713 } else {
714 break;
715 }
716 }
717 if self.input.get(self.at) != Some(&b'"') {
718 return Err(Self::error(start, "invalid binary literal"));
719 }
720 let mut raw = self
721 .budget
722 .alloc_filled(digit_count, HexDigit(0), "step_binary_hex_digits")
723 .map_err(|error| Self::resource_error(start, error))?;
724 let mut cursor = content;
725 let mut written = 0usize;
726 while cursor < self.at {
727 let byte = self.input[cursor];
728 if let Some(digit) = HexDigit::new(byte) {
729 raw[written] = digit;
730 written += 1;
731 cursor += 1;
732 } else if byte.is_ascii_control() {
733 cursor += 1;
734 } else if byte == b'\\' {
735 let Some(end) = self.print_control_end(cursor) else {
736 return Err(Self::error(cursor, "invalid binary literal"));
737 };
738 cursor = end;
739 } else {
740 return Err(Self::error(cursor, "invalid binary literal"));
741 }
742 }
743 let Some((&indicator, digits)) = raw.split_first() else {
744 return Err(Self::error(
745 start,
746 "binary literal has no unused-bit indicator",
747 ));
748 };
749 let unused_bits = indicator.nibble();
750 if unused_bits > 3 {
751 return Err(Self::error(
752 start,
753 "binary unused-bit indicator exceeds three",
754 ));
755 }
756 if digits.is_empty() && unused_bits != 0 {
757 return Err(Self::error(start, "empty binary payload has unused bits"));
758 }
759 if unused_bits != 0
760 && digits
761 .last()
762 .is_some_and(|digit| digit.nibble() & ((1 << unused_bits) - 1) != 0)
763 {
764 return Err(Self::error(start, "unused binary bits are not zero"));
765 }
766 let packed_len = digits.len().div_ceil(2);
767 let mut data = self
768 .budget
769 .alloc_filled(packed_len, 0_u8, "step_binary_packed_bytes")
770 .map_err(|error| Self::resource_error(start, error))?;
771 let mut output = 0usize;
772 let mut pairs = digits.chunks_exact(2);
773 for pair in &mut pairs {
774 data[output] = (pair[0].nibble() << 4) | pair[1].nibble();
775 output += 1;
776 }
777 if let [last] = pairs.remainder() {
778 data[output] = last.nibble() << 4;
779 }
780 let unused_bits = unused_bits + if digits.len() % 2 == 1 { 4 } else { 0 };
781 self.at += 1;
782 Ok(TokenKind::Binary(BinaryValue {
783 unused_bits,
784 data: data.into_boxed_slice(),
785 }))
786 }
787
788 fn resource(&mut self) -> Result<TokenKind, LexError> {
789 let start = self.at;
790 self.resource_value()
791 .or_else(|error| self.recover_metadata_literal(start, b'>', error))
792 }
793
794 fn resource_value(&mut self) -> Result<TokenKind, LexError> {
795 let start = self.at;
796 self.at += 1;
797 let content = self.at;
798 let mut value_len = 0usize;
799 while let Some(byte) = self.input.get(self.at).copied() {
800 if byte == b'>' {
801 break;
802 }
803 if self.print_control_end(self.at).is_some() {
804 return Err(Self::error(
805 self.at,
806 "print control directive is not allowed in a resource",
807 ));
808 }
809 if !byte.is_ascii_control() {
810 value_len += 1;
811 }
812 self.at += 1;
813 }
814 if self.input.get(self.at) != Some(&b'>') {
815 return Err(Self::error(start, "unterminated resource token"));
816 }
817 let mut value = self
818 .budget
819 .alloc_filled(value_len, 0_u8, "step_uri_lexeme_bytes")
820 .map_err(|error| Self::resource_error(start, error))?;
821 let mut written = 0usize;
822 for &byte in &self.input[content..self.at] {
823 if !byte.is_ascii_control() {
824 value[written] = byte;
825 written += 1;
826 }
827 }
828 let value = String::from_utf8(value)
829 .map_err(|_| Self::error(content, "resource token is not UTF-8"))?;
830 self.at += 1;
831 Ok(TokenKind::Resource(value))
832 }
833
834 fn skip_ignored(&mut self) {
835 while self.input.get(self.at).is_some_and(u8::is_ascii_control) {
836 self.at += 1;
837 }
838 }
839
840 fn next_non_ignored(&self, mut at: usize) -> Option<u8> {
841 while self.input.get(at).is_some_and(u8::is_ascii_control) {
842 at += 1;
843 }
844 self.input.get(at).copied()
845 }
846
847 fn normalized(&self, start: usize, end: usize) -> String {
848 self.input[start..end]
849 .iter()
850 .filter(|byte| !byte.is_ascii_control())
851 .map(|byte| char::from(*byte))
852 .collect()
853 }
854
855 fn print_control_end(&self, at: usize) -> Option<usize> {
856 print_control_end(self.input, at)
857 }
858
859 fn match_exact_ignoring_controls(&self, at: usize, expected: &[u8]) -> Option<usize> {
860 match_exact_ignoring_controls(self.input, at, expected)
861 }
862
863 fn error(offset: usize, message: &str) -> LexError {
864 LexError {
865 offset,
866 message: message.into(),
867 resource: None,
868 }
869 }
870
871 fn resource_error(offset: usize, error: CodecError) -> LexError {
872 LexError {
873 offset,
874 message: error.to_string(),
875 resource: Some(error),
876 }
877 }
878}
879
880#[derive(Debug, Clone, Copy, PartialEq, Eq)]
886struct HexDigit(u8);
887
888impl HexDigit {
889 const fn new(byte: u8) -> Option<Self> {
891 Some(Self(match byte {
892 b'0'..=b'9' => byte - b'0',
893 b'a'..=b'f' => byte - b'a' + 10,
894 b'A'..=b'F' => byte - b'A' + 10,
895 _ => return None,
896 }))
897 }
898
899 const fn nibble(self) -> u8 {
901 self.0
902 }
903}
904
905pub(crate) fn match_exact_ignoring_controls(
906 input: &[u8],
907 mut at: usize,
908 expected: &[u8],
909) -> Option<usize> {
910 for &byte in expected {
911 while input.get(at).is_some_and(u8::is_ascii_control) {
912 at += 1;
913 }
914 if input.get(at) != Some(&byte) {
915 return None;
916 }
917 at += 1;
918 }
919 Some(at)
920}
921
922pub(crate) fn match_ignoring_controls(
923 input: &[u8],
924 mut at: usize,
925 expected: &[u8],
926) -> Option<usize> {
927 for &byte in expected {
928 while input.get(at).is_some_and(u8::is_ascii_control) {
929 at += 1;
930 }
931 if !input
932 .get(at)
933 .is_some_and(|value| value.eq_ignore_ascii_case(&byte))
934 {
935 return None;
936 }
937 at += 1;
938 }
939 Some(at)
940}
941
942#[cfg(test)]
943mod tests;