1use super::{
2 constants,
3 handle::NodeHandle,
4 tag::{Attributes, HTMLTag, Node},
5};
6use crate::InnerNodeHandle;
7use crate::inline::hashmap::InlineHashMap;
8use crate::{ParseError, bytes::Bytes, inline::vec::InlineVec, simd};
9use crate::{ParserOptions, stream::Stream};
10
11#[cfg(feature = "std")]
12type StorageVec<T, const N: usize> = std::vec::Vec<T>;
13#[cfg(not(feature = "std"))]
14type StorageVec<T, const N: usize> = InlineVec<T, N>;
15
16#[cfg(feature = "std")]
17type StorageMap<K, V, const N: usize> = InlineHashMap<K, V, N>;
18#[cfg(not(feature = "std"))]
19type StorageMap<K, V, const N: usize> = InlineHashMap<K, V, N>;
20
21#[cfg(feature = "std")]
22fn new_vec<T, const N: usize>() -> StorageVec<T, N> {
23 std::vec::Vec::new()
24}
25
26#[cfg(not(feature = "std"))]
27fn new_vec<T, const N: usize>() -> StorageVec<T, N> {
28 InlineVec::new()
29}
30
31#[cfg(feature = "std")]
32fn new_map<K, V, const N: usize>() -> StorageMap<K, V, N>
33where
34 K: core::hash::Hash + Eq,
35{
36 InlineHashMap::new()
37}
38
39#[cfg(not(feature = "std"))]
40fn new_map<K, V, const N: usize>() -> StorageMap<K, V, N>
41where
42 K: core::hash::Hash + Eq,
43{
44 InlineHashMap::new()
45}
46
47#[cfg(feature = "std")]
48fn push_vec<T, const N: usize>(
49 vec: &mut StorageVec<T, N>,
50 value: T,
51 _err: ParseError,
52) -> Result<(), ParseError> {
53 vec.push(value);
54 Ok(())
55}
56
57#[cfg(not(feature = "std"))]
58fn push_vec<T, const N: usize>(
59 vec: &mut StorageVec<T, N>,
60 value: T,
61 err: ParseError,
62) -> Result<(), ParseError> {
63 vec.push(value).map_err(|_| err)
64}
65
66#[cfg(feature = "std")]
67fn insert_map<K, V, const N: usize>(
68 map: &mut StorageMap<K, V, N>,
69 key: K,
70 value: V,
71 err: ParseError,
72) -> Result<Option<V>, ParseError>
73where
74 K: core::hash::Hash + Eq,
75{
76 if let Some(slot) = map.get_mut(&key) {
77 let old = core::mem::replace(slot, value);
78 Ok(Some(old))
79 } else {
80 map.insert(key, value).map_err(|_| err)?;
81 Ok(None)
82 }
83}
84
85#[cfg(not(feature = "std"))]
86fn insert_map<K, V, const N: usize>(
87 map: &mut StorageMap<K, V, N>,
88 key: K,
89 value: V,
90 err: ParseError,
91) -> Result<Option<V>, ParseError>
92where
93 K: core::hash::Hash + Eq,
94{
95 if let Some(slot) = map.get_mut(&key) {
96 let old = core::mem::replace(slot, value);
97 Ok(Some(old))
98 } else {
99 map.insert(key, value).map_err(|_| err)?;
100 Ok(None)
101 }
102}
103
104pub type Tree<'a, const MAX_NODES: usize = 0> = StorageVec<Node<'a>, MAX_NODES>;
106
107pub type ClassVec<const MAX_NODES: usize = 0> = InlineVec<NodeHandle, MAX_NODES>;
109
110#[derive(Debug, Copy, Clone, PartialEq)]
112#[repr(C)]
113pub enum HTMLVersion {
114 HTML5,
116 StrictHTML401,
118 TransitionalHTML401,
120 FramesetHTML401,
122}
123#[derive(Debug)]
128pub struct Parser<
129 'a,
130 const MAX_NODES: usize = 0,
131 const MAX_STACK: usize = 0,
132 const MAX_ROOTS: usize = 0,
133 const MAX_IDS: usize = 0,
134 const MAX_CLASSES: usize = 0,
135 const MAX_SELECTOR_NODES: usize = 0,
136> {
137 pub(crate) stream: Stream<'a, u8>,
139 pub(crate) stack: StorageVec<NodeHandle, MAX_STACK>,
140 pub(crate) options: ParserOptions,
142 pub(crate) tags: Tree<'a, MAX_NODES>,
146 pub(crate) ast: StorageVec<NodeHandle, MAX_ROOTS>,
148 pub(crate) ids: StorageMap<Bytes<'a>, NodeHandle, MAX_IDS>,
150 pub(crate) classes: StorageMap<Bytes<'a>, ClassVec<MAX_NODES>, MAX_CLASSES>,
152 pub(crate) version: Option<HTMLVersion>,
154}
155
156impl<
157 'a,
158 const MAX_NODES: usize,
159 const MAX_STACK: usize,
160 const MAX_ROOTS: usize,
161 const MAX_IDS: usize,
162 const MAX_CLASSES: usize,
163 const MAX_SELECTOR_NODES: usize,
164> Parser<'a, MAX_NODES, MAX_STACK, MAX_ROOTS, MAX_IDS, MAX_CLASSES, MAX_SELECTOR_NODES>
165{
166 pub(crate) fn new(input: &'a str, options: ParserOptions) -> Self {
167 Parser {
168 stack: new_vec::<NodeHandle, MAX_STACK>(),
169 options,
170 tags: new_vec::<Node<'a>, MAX_NODES>(),
171 stream: Stream::new(input.as_bytes()),
172 ast: new_vec::<NodeHandle, MAX_ROOTS>(),
173 ids: new_map::<Bytes<'a>, NodeHandle, MAX_IDS>(),
174 classes: new_map::<Bytes<'a>, ClassVec<MAX_NODES>, MAX_CLASSES>(),
175 version: None,
176 }
177 }
178
179 #[inline(always)]
180 fn register_tag(&mut self, node: Node<'a>) -> Result<NodeHandle, ParseError> {
181 push_vec::<Node<'a>, MAX_NODES>(&mut self.tags, node, ParseError::NodeCapacityExceeded)?;
182 Ok(NodeHandle::new((self.tags.len() - 1) as u32))
183 }
184
185 #[inline(always)]
186 fn skip_whitespaces(&mut self) {
187 self.read_while2(b' ', b'\n');
188 }
189
190 fn read_to(&mut self, needle: u8) -> &'a [u8] {
191 let start = self.stream.idx;
192 let bytes = &self.stream.data()[start..];
193
194 let end = simd::find(bytes, needle).unwrap_or_else(|| self.stream.len() - start);
195
196 self.stream.idx += end;
197 self.stream.slice(start, start + end)
198 }
199
200 fn read_to3(&mut self, needle: [u8; 3]) -> &'a [u8] {
201 let start = self.stream.idx;
202 let bytes = &self.stream.data()[start..];
203
204 let end = simd::find3(bytes, needle).unwrap_or_else(|| self.stream.len() - start);
205
206 self.stream.idx += end;
207 self.stream.slice(start, start + end)
208 }
209
210 fn read_while2(&mut self, needle1: u8, needle2: u8) -> Option<()> {
211 loop {
212 let ch = self.stream.current_cpy()?;
213
214 let eq1 = ch == needle1;
215 let eq2 = ch == needle2;
216
217 if !eq1 & !eq2 {
218 return Some(());
219 }
220
221 self.stream.advance();
222 }
223 }
224
225 fn read_ident(&mut self) -> Option<&'a [u8]> {
226 let start = self.stream.idx;
227 let bytes = &self.stream.data()[start..];
228
229 let end = simd::search_non_ident(bytes).unwrap_or_else(|| self.stream.len() - start);
232
233 if end == 0 {
235 return None;
236 }
237
238 self.stream.idx += end;
239 Some(self.stream.slice(start, start + end))
240 }
241
242 fn skip_comment_with_start(&mut self, start: usize) -> &'a [u8] {
243 while !self.stream.is_eof() {
244 let idx = self.stream.idx;
245
246 if self
247 .stream
248 .slice_len(idx, constants::COMMENT.len())
249 .eq(constants::COMMENT)
250 {
251 self.stream.advance_by(constants::COMMENT.len());
252
253 let is_end_of_comment = self.stream.expect_and_skip_cond(b'>');
254
255 if is_end_of_comment {
256 return self.stream.slice(start, self.stream.idx);
257 }
258 }
259
260 self.stream.advance();
261 }
262
263 &[]
264 }
265
266 fn parse_attribute(&mut self) -> Option<(&'a [u8], Option<&'a [u8]>)> {
267 let name = self.read_ident()?;
268 self.skip_whitespaces();
269
270 let has_value = self.stream.expect_and_skip_cond(b'=');
271 if !has_value {
272 return Some((name, None));
273 }
274
275 self.skip_whitespaces();
276
277 let value = if let Some(quote) = self.stream.expect_oneof_and_skip(b"\"'") {
278 self.read_to(quote)
279 } else {
280 self.read_to3([b' ', b'\n', b'>'])
281 };
282
283 Some((name, Some(value)))
284 }
285
286 fn parse_attributes(&mut self) -> Result<Option<Attributes<'a>>, ParseError> {
287 let mut attributes = Attributes::new();
288
289 loop {
290 self.skip_whitespaces();
291
292 let cur = match self.stream.current_cpy() {
293 Some(cur) => cur,
294 None => return Ok(None),
295 };
296
297 if simd::is_closing(cur) {
298 break;
299 }
300
301 if let Some((key, value)) = self.parse_attribute() {
302 let has_value = value.is_some();
303 let value: Option<Bytes<'a>> = value.map(Into::into);
304
305 match key {
306 b"id" => attributes.id = value,
307 b"class" => attributes.class = value,
308 _ => attributes
309 .raw
310 .insert(key.into(), value)
311 .map_err(|_| ParseError::AttributeCapacityExceeded)?,
312 };
313
314 let Some(cur) = self.stream.current_cpy() else {
316 return Ok(None);
317 };
318 if has_value && !simd::is_closing(cur) {
319 self.stream.advance();
320 }
321 } else {
322 self.stream.advance();
324 }
325 }
326
327 Ok(Some(attributes))
328 }
329
330 #[inline]
331 fn add_to_parent(&mut self, handle: NodeHandle) -> Result<(), ParseError> {
332 if let Some(last) = self.stack.last() {
333 let last = self
334 .tags
335 .get_mut(last.get_inner() as usize)
336 .unwrap()
337 .as_tag_mut()
338 .unwrap();
339
340 last._children
341 .push(handle)
342 .map_err(|_| ParseError::ChildCapacityExceeded)?;
343 } else {
344 push_vec::<NodeHandle, MAX_ROOTS>(
345 &mut self.ast,
346 handle,
347 ParseError::RootCapacityExceeded,
348 )?;
349 }
350 Ok(())
351 }
352
353 fn read_end(&mut self) -> Result<(), ParseError> {
354 self.stream.advance();
355
356 let closing_tag_name = self.read_to(b'>');
357
358 self.stream.expect_and_skip_cond(b'>');
359
360 let closing_tag_matches_parent = self
361 .stack
362 .last()
363 .and_then(|last_handle| last_handle.get(self))
364 .and_then(|last_item| last_item.as_tag())
365 .is_some_and(|last_tag| last_tag.name() == closing_tag_name);
366
367 if !closing_tag_matches_parent {
368 return Ok(());
369 }
370
371 if let Some(handle) = self.stack.pop() {
372 let tag = self
373 .tags
374 .get_mut(handle.get_inner() as usize)
375 .unwrap()
376 .as_tag_mut()
377 .unwrap();
378
379 let ptr = self.stream.data().as_ptr() as usize;
380 let offset = tag._raw.as_ptr() as usize;
381 let offset = offset - ptr;
382
383 tag._raw = self.stream.slice(offset, self.stream.idx).into();
384
385 let (track_classes, track_ids) = (
386 self.options.is_tracking_classes(),
387 self.options.is_tracking_ids(),
388 );
389
390 if let (true, Some(bytes)) = (track_classes, &tag._attributes.class) {
391 let s = bytes
392 .as_bytes_borrowed()
393 .and_then(|x| core::str::from_utf8(x).ok())
394 .map(|x| x.split_ascii_whitespace());
395
396 if let Some(s) = s {
397 for class in s {
398 let key = Bytes::from(class);
399 if let Some(handles) = self.classes.get_mut(&key) {
400 handles
401 .push(handle)
402 .map_err(|_| ParseError::ClassCapacityExceeded)?;
403 } else {
404 let mut handles = ClassVec::<MAX_NODES>::new();
405 handles
406 .push(handle)
407 .map_err(|_| ParseError::ClassCapacityExceeded)?;
408 insert_map::<Bytes<'a>, ClassVec<MAX_NODES>, MAX_CLASSES>(
409 &mut self.classes,
410 key,
411 handles,
412 ParseError::ClassCapacityExceeded,
413 )?;
414 }
415 }
416 }
417 }
418
419 if let (true, Some(bytes)) = (track_ids, &tag._attributes.id) {
420 insert_map::<Bytes<'a>, NodeHandle, MAX_IDS>(
421 &mut self.ids,
422 bytes.clone(),
423 handle,
424 ParseError::IdCapacityExceeded,
425 )?;
426 }
427 }
428 Ok(())
429 }
430
431 #[cold]
432 #[inline(never)]
433 fn read_markdown(&mut self) -> Result<Option<()>, ParseError> {
434 let start = self.stream.idx - 1; self.stream.advance(); let is_comment = self
439 .stream
440 .slice_len(self.stream.idx, 2)
441 .eq(constants::COMMENT);
442
443 if is_comment {
444 let comment = self.skip_comment_with_start(start);
445 let comment = self.register_tag(Node::Comment(comment.into()))?;
446 self.add_to_parent(comment)?;
447 } else {
448 let Some(tag) = self.read_ident() else {
449 return Ok(None);
450 };
451
452 self.skip_whitespaces();
453
454 if simd::matches_case_insensitive(tag, *b"doctype") {
455 let Some(doctype) = self.read_ident() else {
456 return Ok(None);
457 };
458
459 let html5 = simd::matches_case_insensitive(doctype, *b"html");
460
461 if html5 {
462 self.version = Some(HTMLVersion::HTML5);
463 }
464
465 self.skip_whitespaces();
466 self.stream.advance(); }
468 }
469
470 Ok(Some(()))
471 }
472
473 fn parse_tag(&mut self) -> Result<Option<()>, ParseError> {
474 let start = self.stream.idx;
475
476 self.stream.advance();
477 self.skip_whitespaces();
478 let Some(cur) = self.stream.current_cpy() else {
479 return Ok(None);
480 };
481
482 match cur {
483 b'/' => self.read_end()?,
484 b'!' => {
485 self.read_markdown()?;
486 }
487 _ => {
488 let Some(name) = self.read_ident() else {
489 return Ok(None);
490 };
491 self.skip_whitespaces();
492
493 let Some(attr) = self.parse_attributes()? else {
494 return Ok(None);
495 };
496
497 let is_self_closing = self.stream.expect_and_skip_cond(b'/');
498
499 if self.stream.expect_and_skip(b'>').is_none() {
500 return Ok(None);
501 }
502
503 let this = self.register_tag(Node::Tag(HTMLTag::new(
504 name.into(),
505 attr,
506 InlineVec::new(),
507 self.stream.slice(start, self.stream.idx).into(),
508 )))?;
509
510 self.add_to_parent(this)?;
511
512 if !is_self_closing && !constants::VOID_TAGS.contains(&name) {
517 push_vec::<NodeHandle, MAX_STACK>(
518 &mut self.stack,
519 this,
520 ParseError::StackCapacityExceeded,
521 )?;
522 }
523 }
524 };
525
526 Ok(Some(()))
527 }
528
529 pub(crate) fn parse_single(&mut self) -> Result<Option<()>, ParseError> {
530 loop {
531 let Some(cur) = self.stream.current() else {
532 return Ok(None);
533 };
534
535 if *cur == b'<' {
536 self.parse_tag()?;
537 } else {
538 let raw = Node::Raw(self.read_to(b'<').into());
539 let handle = self.register_tag(raw)?;
540 self.add_to_parent(handle)?;
541 }
542 }
543 }
544
545 #[inline]
547 pub fn resolve_node_id(&self, id: InnerNodeHandle) -> Option<&Node<'a>> {
548 self.tags.get(id as usize)
549 }
550
551 #[inline]
553 pub fn resolve_node_id_mut(&mut self, id: InnerNodeHandle) -> Option<&mut Node<'a>> {
554 self.tags.get_mut(id as usize)
555 }
556
557 pub(crate) fn parse(&mut self) -> Result<(), ParseError> {
558 if self.stream.len() > u32::MAX as usize {
559 return Err(ParseError::InvalidLength);
560 }
561
562 while !self.stream.is_eof() {
563 self.parse_single()?;
564 }
565
566 Ok(())
567 }
568}