1use super::{
2 constants,
3 handle::NodeHandle,
4 tag::{Attributes, HTMLTag, Node},
5};
6use crate::InnerNodeHandle;
7use crate::{bytes::Bytes, inline::vec::InlineVec, simd, ParseError};
8use crate::{stream::Stream, ParserOptions};
9use std::collections::HashMap;
10
11pub type Tree<'a> = Vec<Node<'a>>;
13
14pub type ClassVec = InlineVec<NodeHandle, 2>;
16
17#[derive(Debug, Copy, Clone, PartialEq)]
19#[repr(C)]
20pub enum HTMLVersion {
21 HTML5,
23 StrictHTML401,
25 TransitionalHTML401,
27 FramesetHTML401,
29}
30#[derive(Debug)]
35pub struct Parser<'a> {
36 pub(crate) stream: Stream<'a, u8>,
38 pub(crate) stack: Vec<NodeHandle>,
39 pub(crate) options: ParserOptions,
41 pub(crate) tags: Tree<'a>,
45 pub(crate) ast: Vec<NodeHandle>,
47 pub(crate) ids: HashMap<Bytes<'a>, NodeHandle>,
49 pub(crate) classes: HashMap<Bytes<'a>, ClassVec>,
51 pub(crate) version: Option<HTMLVersion>,
53}
54
55impl<'a> Parser<'a> {
56 pub(crate) fn new(input: &str, options: ParserOptions) -> Parser<'_> {
57 Parser {
58 stack: Vec::with_capacity(4),
59 options,
60 tags: Vec::new(),
61 stream: Stream::new(input.as_bytes()),
62 ast: Vec::new(),
63 ids: HashMap::new(),
64 classes: HashMap::new(),
65 version: None,
66 }
67 }
68
69 #[inline(always)]
70 fn register_tag(&mut self, node: Node<'a>) -> NodeHandle {
71 self.tags.push(node);
72 NodeHandle::new((self.tags.len() - 1) as u32)
73 }
74
75 #[inline(always)]
76 fn skip_whitespaces(&mut self) {
77 self.read_while2(b' ', b'\n');
78 }
79
80 fn read_to(&mut self, needle: u8) -> &'a [u8] {
81 let start = self.stream.idx;
82 let bytes = &self.stream.data()[start..];
83
84 let end = simd::find(bytes, needle).unwrap_or_else(|| self.stream.len() - start);
85
86 self.stream.idx += end;
87 self.stream.slice(start, start + end)
88 }
89
90 fn read_to3(&mut self, needle: [u8; 3]) -> &'a [u8] {
91 let start = self.stream.idx;
92 let bytes = &self.stream.data()[start..];
93
94 let end = simd::find3(bytes, needle).unwrap_or_else(|| self.stream.len() - start);
95
96 self.stream.idx += end;
97 self.stream.slice(start, start + end)
98 }
99
100 fn read_while2(&mut self, needle1: u8, needle2: u8) -> Option<()> {
101 loop {
102 let ch = self.stream.current_cpy()?;
103
104 let eq1 = ch == needle1;
105 let eq2 = ch == needle2;
106
107 if !eq1 & !eq2 {
108 return Some(());
109 }
110
111 self.stream.advance();
112 }
113 }
114
115 fn read_ident(&mut self) -> Option<&'a [u8]> {
116 let start = self.stream.idx;
117 let bytes = &self.stream.data()[start..];
118
119 let end = simd::search_non_ident(bytes).unwrap_or_else(|| self.stream.len() - start);
122
123 if end == 0 {
125 return None;
126 }
127
128 self.stream.idx += end;
129 Some(self.stream.slice(start, start + end))
130 }
131
132 fn skip_comment_with_start(&mut self, start: usize) -> &'a [u8] {
133 while !self.stream.is_eof() {
134 let idx = self.stream.idx;
135
136 if self
137 .stream
138 .slice_len(idx, constants::COMMENT.len())
139 .eq(constants::COMMENT)
140 {
141 self.stream.advance_by(constants::COMMENT.len());
142
143 let is_end_of_comment = self.stream.expect_and_skip_cond(b'>');
144
145 if is_end_of_comment {
146 return self.stream.slice(start, self.stream.idx);
147 }
148 }
149
150 self.stream.advance();
151 }
152
153 &[]
154 }
155
156 fn parse_attribute(&mut self) -> Option<(&'a [u8], Option<&'a [u8]>)> {
157 let name = self.read_ident()?;
158 self.skip_whitespaces();
159
160 let has_value = self.stream.expect_and_skip_cond(b'=');
161 if !has_value {
162 return Some((name, None));
163 }
164
165 self.skip_whitespaces();
166
167 let value = if let Some(quote) = self.stream.expect_oneof_and_skip(b"\"'") {
168 self.read_to(quote)
169 } else {
170 self.read_to3(*b" \n>")
171 };
172
173 Some((name, Some(value)))
174 }
175
176 fn parse_attributes(&mut self) -> Option<Attributes<'a>> {
177 let mut attributes = Attributes::new();
178
179 loop {
180 self.skip_whitespaces();
181
182 let cur = self.stream.current_cpy()?;
183
184 if simd::is_closing(cur) {
185 break;
186 }
187
188 if let Some((key, value)) = self.parse_attribute() {
189 let has_value = value.is_some();
190 let value: Option<Bytes<'a>> = value.map(Into::into);
191 let key = Bytes::from(key).into_ascii_lowercase();
192
193 match key.as_bytes() {
194 b"id" => {
195 if attributes.id.is_none() {
196 attributes.id = Some(value);
197 }
198 }
199 b"class" => {
200 if attributes.class.is_none() {
201 attributes.class = Some(value);
202 }
203 }
204 _ => {
205 if !attributes.raw.contains_key(&key) {
206 attributes.raw.insert(key, value);
207 }
208 }
209 }
210
211 if has_value && !simd::is_closing(self.stream.current_cpy()?) {
213 self.stream.advance();
214 }
215 } else {
216 self.stream.advance();
218 }
219 }
220
221 Some(attributes)
222 }
223
224 #[inline]
225 fn add_to_parent(&mut self, handle: NodeHandle) {
226 if let Some(last) = self.stack.last() {
227 let last = self
228 .tags
229 .get_mut(last.get_inner() as usize)
230 .unwrap()
231 .as_tag_mut()
232 .unwrap();
233
234 last._children.push(handle);
235 } else {
236 self.ast.push(handle);
237 }
238 }
239
240 fn read_end(&mut self) {
241 self.stream.advance();
242
243 let closing_tag_name = self.read_to(b'>');
244
245 self.stream.expect_and_skip_cond(b'>');
246
247 let closing_tag_matches_parent = self
248 .stack
249 .last()
250 .and_then(|last_handle| last_handle.get(self))
251 .and_then(|last_item| last_item.as_tag())
252 .is_some_and(|last_tag| last_tag.name() == closing_tag_name);
253
254 if !closing_tag_matches_parent {
255 return;
256 }
257
258 if let Some(handle) = self.stack.pop() {
259 let tag = self
260 .tags
261 .get_mut(handle.get_inner() as usize)
262 .unwrap()
263 .as_tag_mut()
264 .unwrap();
265
266 let ptr = self.stream.data().as_ptr() as usize;
267 let offset = tag._raw.as_ptr() as usize;
268 let offset = offset - ptr;
269
270 tag._raw = self.stream.slice(offset, self.stream.idx).into();
271
272 let (track_classes, track_ids) = (
273 self.options.is_tracking_classes(),
274 self.options.is_tracking_ids(),
275 );
276
277 if let (true, Some(Some(bytes))) = (track_classes, &tag._attributes.class) {
278 let s = bytes
279 .as_bytes_borrowed()
280 .and_then(|x| std::str::from_utf8(x).ok())
281 .map(|x| x.split_ascii_whitespace());
282
283 if let Some(s) = s {
284 for class in s {
285 self.classes
286 .entry(class.into())
287 .or_insert_with(InlineVec::new)
288 .push(handle);
289 }
290 }
291 }
292
293 if let (true, Some(Some(bytes))) = (track_ids, &tag._attributes.id) {
294 self.ids.insert(bytes.clone(), handle);
295 }
296 }
297 }
298
299 #[cold]
300 #[inline(never)]
301 fn read_markdown(&mut self) -> Option<()> {
302 let start = self.stream.idx - 1; self.stream.advance(); let is_comment = self
307 .stream
308 .slice_len(self.stream.idx, 2)
309 .eq(constants::COMMENT);
310
311 if is_comment {
312 let comment = self.skip_comment_with_start(start);
313 let comment = self.register_tag(Node::Comment(comment.into()));
314 self.add_to_parent(comment);
315 } else {
316 let tag = self.read_ident()?;
317
318 self.skip_whitespaces();
319
320 if simd::matches_case_insensitive(tag, *b"doctype") {
321 let doctype = self.read_ident()?;
322
323 let html5 = simd::matches_case_insensitive(doctype, *b"html");
324
325 if html5 {
326 self.version = Some(HTMLVersion::HTML5);
327 }
328
329 self.skip_whitespaces();
330 self.stream.advance(); }
332 }
333
334 Some(())
335 }
336
337 fn parse_tag(&mut self) -> Option<()> {
338 let start = self.stream.idx;
339
340 self.stream.advance();
341 self.skip_whitespaces();
342 let cur = self.stream.current_cpy()?;
343
344 match cur {
345 b'/' => self.read_end(),
346 b'!' => {
347 self.read_markdown();
348 }
349 _ => {
350 let name = self.read_ident()?;
351 self.skip_whitespaces();
352
353 let attr = self.parse_attributes()?;
354
355 let is_self_closing = self.stream.expect_and_skip_cond(b'/');
356
357 self.stream.expect_and_skip(b'>')?;
358
359 let this = self.register_tag(Node::Tag(HTMLTag::new(
360 name.into(),
361 attr,
362 InlineVec::new(),
363 self.stream.slice(start, self.stream.idx).into(),
364 )));
365
366 self.add_to_parent(this);
367
368 if !is_self_closing && !constants::VOID_TAGS.contains(&name) {
373 self.stack.push(this);
374 }
375 }
376 };
377
378 Some(())
379 }
380
381 pub(crate) fn parse_single(&mut self) -> Option<()> {
382 loop {
383 let cur = self.stream.current()?;
384
385 if *cur == b'<' {
386 self.parse_tag();
387 } else {
388 let raw = Node::Raw(self.read_to(b'<').into());
389 let handle = self.register_tag(raw);
390 self.add_to_parent(handle);
391 }
392 }
393 }
394
395 #[inline]
397 pub fn resolve_node_id(&self, id: InnerNodeHandle) -> Option<&Node<'a>> {
398 self.tags.get(id as usize)
399 }
400
401 #[inline]
403 pub fn resolve_node_id_mut(&mut self, id: InnerNodeHandle) -> Option<&mut Node<'a>> {
404 self.tags.get_mut(id as usize)
405 }
406
407 pub(crate) fn parse(&mut self) -> Result<(), ParseError> {
408 if self.stream.len() > u32::MAX as usize {
409 return Err(ParseError::InvalidLength);
410 }
411
412 while !self.stream.is_eof() {
413 self.parse_single();
414 }
415
416 Ok(())
417 }
418}