ifc_lite_core/parser/
header_scan.rs1use crate::parser::is_step_space;
5
6pub struct Lex<'a> {
11 bytes: &'a [u8],
12 no_closer: bool,
13 pub searches: u32,
15}
16
17impl<'a> Lex<'a> {
18 pub fn new(bytes: &'a [u8]) -> Self {
19 Lex {
20 bytes,
21 no_closer: false,
22 searches: 0,
23 }
24 }
25
26 fn skip_comment_at(&mut self, i: usize) -> Option<usize> {
29 if self.bytes.get(i) != Some(&b'/') || self.bytes.get(i + 1) != Some(&b'*') {
30 return None;
31 }
32 if self.no_closer {
33 return None;
34 }
35 self.searches += 1;
36 match crate::parser::skip_step_comment(self.bytes, i) {
37 Some(end) => Some(end),
38 None => {
39 self.no_closer = true;
40 None
41 }
42 }
43 }
44
45 pub fn skip_lexical_at(&mut self, i: usize) -> Option<usize> {
48 if self.bytes.get(i)? == &b'\'' {
49 let mut p = i + 1;
50 while p < self.bytes.len() {
51 if self.bytes[p] != b'\'' || crate::parser::lexical::is_page_quote_operand(self.bytes, p) {
52 p += 1;
53 } else if self.bytes.get(p + 1) == Some(&b'\'') {
54 p += 2;
55 } else {
56 return Some(p + 1);
57 }
58 }
59 return Some(self.bytes.len());
60 }
61 self.skip_comment_at(i)
62 }
63
64 pub fn skip_trivia(&mut self, mut i: usize) -> usize {
66 loop {
67 while i < self.bytes.len() && is_step_space(self.bytes[i]) {
68 i += 1;
69 }
70 match self.skip_comment_at(i) {
71 Some(end) => i = end,
72 None => return i,
73 }
74 }
75 }
76}
77
78fn is_ident(b: u8) -> bool {
79 b.is_ascii_alphanumeric() || b == b'_'
80}
81
82pub fn find_record_open(haystack: &[u8], keyword: &[u8]) -> Option<usize> {
85 if keyword.is_empty() || keyword.len() > haystack.len() {
86 return None;
87 }
88 let mut lex = Lex::new(haystack);
89 let last_start = haystack.len() - keyword.len();
90 let mut i = 0;
91 while i <= last_start {
92 if let Some(end) = lex.skip_lexical_at(i) {
93 i = end;
94 continue;
95 }
96 let end = i + keyword.len();
97 let bounded = (i == 0 || !is_ident(haystack[i - 1]))
98 && (end == haystack.len() || !is_ident(haystack[end]));
99 if bounded
100 && haystack[i..end]
101 .iter()
102 .zip(keyword)
103 .all(|(a, b)| a.eq_ignore_ascii_case(b))
104 {
105 let open = lex.skip_trivia(end);
106 if haystack.get(open) == Some(&b'(') {
107 return Some(open);
108 }
109 }
110 i += 1;
111 }
112 None
113}
114
115pub fn find_section_marker(haystack: &[u8], keyword: &[u8]) -> Option<usize> {
120 if keyword.is_empty() || keyword.len() > haystack.len() {
121 return None;
122 }
123 let mut lex = Lex::new(haystack);
124 let last_start = haystack.len() - keyword.len();
125 let mut i = 0;
126 while i <= last_start {
127 if let Some(end) = lex.skip_lexical_at(i) {
128 i = end;
129 continue;
130 }
131 let before_is_ident =
132 i > 0 && (haystack[i - 1].is_ascii_alphanumeric() || haystack[i - 1] == b'_');
133 let matches = !before_is_ident
134 && haystack[i..i + keyword.len()]
135 .iter()
136 .zip(keyword)
137 .all(|(a, b)| a.eq_ignore_ascii_case(b));
138 if matches {
139 let after = lex.skip_trivia(i + keyword.len());
140 if haystack.get(after) == Some(&b';') {
141 return Some(i);
142 }
143 }
144 i += 1;
145 }
146 None
147}