1use crate::AllocErr;
9use crate::applicable_declarations::{ApplicableDeclarationList, ScopeProximity};
10use crate::context::QuirksMode;
11use crate::derives::*;
12use crate::dom::TElement;
13use crate::rule_tree::CascadeLevel;
14use crate::selector_parser::SelectorImpl;
15use crate::stylist::{CascadeData, ContainerConditionId, Rule, ScopeConditionId, Stylist};
16use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
17use dom::ElementState;
18use precomputed_hash::PrecomputedHash;
19use selectors::matching::MatchingContext;
20use selectors::parser::{Combinator, Component, SelectorIter};
21use smallvec::SmallVec;
22use std::collections::hash_map;
23use std::collections::{HashMap, HashSet};
24use std::hash::{BuildHasherDefault, Hash, Hasher};
25
26#[derive(Default)]
29pub struct PrecomputedHasher {
30 hash: u64,
31 #[cfg(debug_assertions)]
32 initialized: bool,
33}
34
35pub type RelevantAttributes = thin_vec::ThinVec<LocalName>;
37
38const RARE_PSEUDO_CLASS_STATES: ElementState = ElementState::from_bits_retain(
45 ElementState::FULLSCREEN.bits()
46 | ElementState::PICTURE_IN_PICTURE.bits()
47 | ElementState::VISITED_OR_UNVISITED.bits()
48 | ElementState::URLTARGET.bits()
49 | ElementState::INERT.bits()
50 | ElementState::FOCUS.bits()
51 | ElementState::FOCUSRING.bits()
52 | ElementState::TOPMOST_MODAL.bits()
53 | ElementState::SUPPRESS_FOR_PRINT_SELECTION.bits()
54 | ElementState::ACTIVE_VIEW_TRANSITION.bits()
55 | ElementState::HEADING_LEVEL_BITS.bits(),
56);
57
58pub type PrecomputedHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PrecomputedHasher>>;
60
61pub type PrecomputedHashSet<K> = HashSet<K, BuildHasherDefault<PrecomputedHasher>>;
63
64impl Hasher for PrecomputedHasher {
65 #[inline]
66 fn write(&mut self, _: &[u8]) {
67 unreachable!(
68 "Called into PrecomputedHasher with something that isn't \
69 a u64 or u32"
70 )
71 }
72
73 #[inline]
74 fn write_u32(&mut self, i: u32) {
75 #[cfg(debug_assertions)]
76 debug_assert!(!self.initialized);
77 debug_assert_eq!(self.hash, 0);
78 let extended = i as u64;
79 self.hash = (extended << 32) | extended;
80 #[cfg(debug_assertions)]
81 {
82 self.initialized = true;
83 }
84 }
85
86 #[inline]
87 fn write_u64(&mut self, i: u64) {
88 #[cfg(debug_assertions)]
89 debug_assert!(!self.initialized);
90 debug_assert_eq!(self.hash, 0);
91 self.hash = i;
92 #[cfg(debug_assertions)]
93 {
94 self.initialized = true;
95 }
96 }
97
98 #[inline]
99 fn finish(&self) -> u64 {
100 #[cfg(debug_assertions)]
101 debug_assert!(self.initialized);
102 self.hash
103 }
104}
105
106pub trait SelectorMapEntry: Sized + Clone {
108 fn selector(&self) -> SelectorIter<'_, SelectorImpl>;
110 fn set_bucket_matches(&mut self, _: BucketMatches) {}
112}
113
114#[derive(Clone, Debug, MallocSizeOf)]
142pub struct SelectorMap<T: 'static> {
143 pub root: SmallVec<[T; 1]>,
145 pub id_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
147 pub class_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,
149 pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
151 pub attribute_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,
153 pub namespace_hash: PrecomputedHashMap<Namespace, SmallVec<[T; 1]>>,
155 pub rare_pseudo_classes: SmallVec<[T; 1]>,
157 pub other: SmallVec<[T; 1]>,
159 pub count: usize,
161}
162
163impl<T: 'static> Default for SelectorMap<T> {
164 #[inline]
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl<T> SelectorMap<T> {
171 pub fn new() -> Self {
173 SelectorMap {
174 root: SmallVec::new(),
175 id_hash: MaybeCaseInsensitiveHashMap::new(),
176 class_hash: MaybeCaseInsensitiveHashMap::new(),
177 attribute_hash: HashMap::default(),
178 local_name_hash: HashMap::default(),
179 namespace_hash: HashMap::default(),
180 rare_pseudo_classes: SmallVec::new(),
181 other: SmallVec::new(),
182 count: 0,
183 }
184 }
185
186 pub fn shrink_if_needed(&mut self) {
188 self.id_hash.shrink_if_needed();
189 self.class_hash.shrink_if_needed();
190 self.attribute_hash.shrink_if_needed();
191 self.local_name_hash.shrink_if_needed();
192 self.namespace_hash.shrink_if_needed();
193 }
194
195 pub fn clear(&mut self) {
197 self.root.clear();
198 self.id_hash.clear();
199 self.class_hash.clear();
200 self.attribute_hash.clear();
201 self.local_name_hash.clear();
202 self.namespace_hash.clear();
203 self.rare_pseudo_classes.clear();
204 self.other.clear();
205 self.count = 0;
206 }
207
208 pub fn is_empty(&self) -> bool {
210 self.count == 0
211 }
212
213 pub fn len(&self) -> usize {
215 self.count
216 }
217}
218
219impl SelectorMap<Rule> {
220 pub fn get_all_matching_rules<E>(
225 &self,
226 element: E,
227 rule_hash_target: E,
228 matching_rules_list: &mut ApplicableDeclarationList,
229 matching_context: &mut MatchingContext<E::Impl>,
230 cascade_level: CascadeLevel,
231 cascade_data: &CascadeData,
232 stylist: &Stylist,
233 ) where
234 E: TElement,
235 {
236 if self.is_empty() {
237 return;
238 }
239
240 let quirks_mode = matching_context.quirks_mode();
241
242 if rule_hash_target.is_root() {
243 SelectorMap::get_matching_rules(
244 element,
245 &self.root,
246 matching_rules_list,
247 matching_context,
248 cascade_level,
249 cascade_data,
250 stylist,
251 );
252 }
253
254 if let Some(id) = rule_hash_target.id() {
255 if let Some(rules) = self.id_hash.get(id, quirks_mode) {
256 SelectorMap::get_matching_rules(
257 element,
258 rules,
259 matching_rules_list,
260 matching_context,
261 cascade_level,
262 cascade_data,
263 stylist,
264 )
265 }
266 }
267
268 rule_hash_target.each_class(|class| {
269 if let Some(rules) = self.class_hash.get(class, quirks_mode) {
270 SelectorMap::get_matching_rules(
271 element,
272 rules,
273 matching_rules_list,
274 matching_context,
275 cascade_level,
276 cascade_data,
277 stylist,
278 )
279 }
280 });
281
282 rule_hash_target.each_attr_name(|name| {
283 if let Some(rules) = self.attribute_hash.get(name) {
284 SelectorMap::get_matching_rules(
285 element,
286 rules,
287 matching_rules_list,
288 matching_context,
289 cascade_level,
290 cascade_data,
291 stylist,
292 )
293 }
294 });
295
296 if let Some(rules) = self.local_name_hash.get(rule_hash_target.local_name()) {
297 SelectorMap::get_matching_rules(
298 element,
299 rules,
300 matching_rules_list,
301 matching_context,
302 cascade_level,
303 cascade_data,
304 stylist,
305 )
306 }
307
308 if rule_hash_target
309 .state()
310 .intersects(RARE_PSEUDO_CLASS_STATES)
311 {
312 SelectorMap::get_matching_rules(
313 element,
314 &self.rare_pseudo_classes,
315 matching_rules_list,
316 matching_context,
317 cascade_level,
318 cascade_data,
319 stylist,
320 );
321 }
322
323 if let Some(rules) = self.namespace_hash.get(rule_hash_target.namespace()) {
324 SelectorMap::get_matching_rules(
325 element,
326 rules,
327 matching_rules_list,
328 matching_context,
329 cascade_level,
330 cascade_data,
331 stylist,
332 )
333 }
334
335 SelectorMap::get_matching_rules(
336 element,
337 &self.other,
338 matching_rules_list,
339 matching_context,
340 cascade_level,
341 cascade_data,
342 stylist,
343 );
344 }
345
346 pub(crate) fn get_matching_rules<E>(
348 element: E,
349 rules: &[Rule],
350 matching_rules: &mut ApplicableDeclarationList,
351 matching_context: &mut MatchingContext<E::Impl>,
352 cascade_level: CascadeLevel,
353 cascade_data: &CascadeData,
354 stylist: &Stylist,
355 ) where
356 E: TElement,
357 {
358 for rule in rules {
359 let scope_proximity = if rule.scope_condition_id == ScopeConditionId::none() {
360 if !rule.matches_selector(element, matching_context) {
361 continue;
362 }
363 ScopeProximity::infinity()
364 } else {
365 let result =
366 cascade_data.find_scope_proximity_if_matching(rule, element, matching_context);
367 if result == ScopeProximity::infinity() {
368 continue;
369 }
370 result
371 };
372
373 if rule.container_condition_id != ContainerConditionId::none()
374 && !cascade_data.container_condition_matches(
375 rule.container_condition_id,
376 stylist,
377 element,
378 matching_context,
379 )
380 {
381 continue;
382 }
383 matching_rules.push(rule.to_applicable_declaration_block(
384 cascade_level,
385 cascade_data,
386 scope_proximity,
387 ));
388 }
389 }
390}
391
392impl<T: SelectorMapEntry> SelectorMap<T> {
393 pub fn insert(&mut self, mut entry: T, quirks_mode: QuirksMode) -> Result<(), AllocErr> {
395 self.count += 1;
396
397 let mut bucket_matches = BucketMatches::Full;
402 macro_rules! insert_into_bucket {
403 ($entry:ident, $bucket:expr) => {{
404 let vec = match $bucket {
405 Bucket::Root => &mut self.root,
406 Bucket::ID(id) => self
407 .id_hash
408 .try_entry(id.clone(), quirks_mode)?
409 .or_default(),
410 Bucket::Class(class) => self
411 .class_hash
412 .try_entry(class.clone(), quirks_mode)?
413 .or_default(),
414 Bucket::Attribute { name, lower_name }
415 | Bucket::LocalName { name, lower_name } => {
416 let is_attribute = matches!($bucket, Bucket::Attribute { .. });
430 let hash = if is_attribute {
431 &mut self.attribute_hash
432 } else {
433 &mut self.local_name_hash
434 };
435 if name != lower_name {
436 hash.try_reserve(1)?;
437 let vec = hash.entry(lower_name.clone()).or_default();
438 vec.try_reserve(1)?;
439 let mut entry = $entry.clone();
440 entry.set_bucket_matches(bucket_matches);
441 vec.push(entry);
442 }
443 hash.try_reserve(1)?;
444 hash.entry(name.clone()).or_default()
445 },
446 Bucket::Namespace(url) => {
447 self.namespace_hash.try_reserve(1)?;
448 self.namespace_hash.entry(url.clone()).or_default()
449 },
450 Bucket::RarePseudoClasses => &mut self.rare_pseudo_classes,
451 Bucket::Universal => &mut self.other,
452 };
453 vec.try_reserve(1)?;
454 $entry.set_bucket_matches(bucket_matches);
455 vec.push($entry);
456 }};
457 }
458
459 let bucket = {
460 let mut disjoint_buckets = SmallVec::new();
461 let bucket = find_bucket(
462 entry.selector(),
463 quirks_mode,
464 &mut disjoint_buckets,
465 &mut bucket_matches,
466 false,
467 );
468
469 if !disjoint_buckets.is_empty()
485 && disjoint_buckets
486 .iter()
487 .all(|b| b.more_specific_than(&bucket))
488 {
489 for bucket in &disjoint_buckets {
490 let mut entry = entry.clone();
491 insert_into_bucket!(entry, *bucket);
492 }
493 return Ok(());
494 }
495 bucket
496 };
497
498 insert_into_bucket!(entry, bucket);
499 Ok(())
500 }
501
502 pub fn lookup<'a, E, F>(
513 &'a self,
514 element: E,
515 quirks_mode: QuirksMode,
516 relevant_attributes: Option<&mut RelevantAttributes>,
517 f: F,
518 ) -> bool
519 where
520 E: TElement,
521 F: FnMut(&'a T) -> bool,
522 {
523 self.lookup_with_state(
524 element,
525 element.state(),
526 quirks_mode,
527 relevant_attributes,
528 f,
529 )
530 }
531
532 #[inline]
533 fn lookup_with_state<'a, E, F>(
534 &'a self,
535 element: E,
536 element_state: ElementState,
537 quirks_mode: QuirksMode,
538 mut relevant_attributes: Option<&mut RelevantAttributes>,
539 mut f: F,
540 ) -> bool
541 where
542 E: TElement,
543 F: FnMut(&'a T) -> bool,
544 {
545 if element.is_root() {
546 for entry in self.root.iter() {
547 if !f(entry) {
548 return false;
549 }
550 }
551 }
552
553 if let Some(id) = element.id() {
554 if let Some(v) = self.id_hash.get(id, quirks_mode) {
555 for entry in v.iter() {
556 if !f(entry) {
557 return false;
558 }
559 }
560 }
561 }
562
563 let mut done = false;
564 element.each_class(|class| {
565 if done {
566 return;
567 }
568 if let Some(v) = self.class_hash.get(class, quirks_mode) {
569 for entry in v.iter() {
570 if !f(entry) {
571 done = true;
572 return;
573 }
574 }
575 }
576 });
577
578 if done {
579 return false;
580 }
581
582 element.each_attr_name(|name| {
583 if done {
584 return;
585 }
586 if let Some(v) = self.attribute_hash.get(name) {
587 if let Some(ref mut relevant_attributes) = relevant_attributes {
588 relevant_attributes.push(name.clone());
589 }
590 for entry in v.iter() {
591 if !f(entry) {
592 done = true;
593 return;
594 }
595 }
596 }
597 });
598
599 if done {
600 return false;
601 }
602
603 if let Some(v) = self.local_name_hash.get(element.local_name()) {
604 for entry in v.iter() {
605 if !f(entry) {
606 return false;
607 }
608 }
609 }
610
611 if let Some(v) = self.namespace_hash.get(element.namespace()) {
612 for entry in v.iter() {
613 if !f(entry) {
614 return false;
615 }
616 }
617 }
618
619 if element_state.intersects(RARE_PSEUDO_CLASS_STATES) {
620 for entry in self.rare_pseudo_classes.iter() {
621 if !f(entry) {
622 return false;
623 }
624 }
625 }
626
627 for entry in self.other.iter() {
628 if !f(entry) {
629 return false;
630 }
631 }
632
633 true
634 }
635
636 #[inline]
644 pub fn lookup_with_additional<'a, E, F>(
645 &'a self,
646 element: E,
647 quirks_mode: QuirksMode,
648 additional_id: Option<&WeakAtom>,
649 additional_classes: &[Atom],
650 additional_states: ElementState,
651 mut f: F,
652 ) -> bool
653 where
654 E: TElement,
655 F: FnMut(&'a T) -> bool,
656 {
657 if !self.lookup_with_state(
659 element,
660 element.state() | additional_states,
661 quirks_mode,
662 None,
663 &mut f,
664 ) {
665 return false;
666 }
667
668 if let Some(id) = additional_id {
670 if let Some(v) = self.id_hash.get(id, quirks_mode) {
671 for entry in v.iter() {
672 if !f(entry) {
673 return false;
674 }
675 }
676 }
677 }
678
679 for class in additional_classes {
681 if let Some(v) = self.class_hash.get(class, quirks_mode) {
682 for entry in v.iter() {
683 if !f(entry) {
684 return false;
685 }
686 }
687 }
688 }
689
690 true
691 }
692}
693
694#[derive(PartialEq)]
695enum Bucket<'a> {
696 Universal,
697 Namespace(&'a Namespace),
698 RarePseudoClasses,
699 LocalName {
700 name: &'a LocalName,
701 lower_name: &'a LocalName,
702 },
703 Attribute {
704 name: &'a LocalName,
705 lower_name: &'a LocalName,
706 },
707 Class(&'a Atom),
708 ID(&'a Atom),
709 Root,
710}
711
712impl<'a> Bucket<'a> {
713 #[inline]
715 fn specificity(&self) -> usize {
716 match *self {
717 Bucket::Universal => 0,
718 Bucket::Namespace(..) => 1,
719 Bucket::RarePseudoClasses => 2,
720 Bucket::LocalName { .. } => 3,
721 Bucket::Attribute { .. } => 4,
722 Bucket::Class(..) => 5,
723 Bucket::ID(..) => 6,
724 Bucket::Root => 7,
725 }
726 }
727
728 #[inline]
729 fn more_or_equally_specific_than(&self, other: &Self) -> bool {
730 self.specificity() >= other.specificity()
731 }
732
733 #[inline]
734 fn more_specific_than(&self, other: &Self) -> bool {
735 self.specificity() > other.specificity()
736 }
737}
738
739type DisjointBuckets<'a> = SmallVec<[Bucket<'a>; 5]>;
740
741#[derive(Copy, Clone, Debug, PartialEq, MallocSizeOf)]
743pub enum BucketMatches {
744 Full,
746 Subject,
748 Unknown,
750}
751
752fn specific_bucket_for<'a>(
753 component: &'a Component<SelectorImpl>,
754 quirks_mode: QuirksMode,
755 disjoint_buckets: &mut DisjointBuckets<'a>,
756 bucket_matches: &mut BucketMatches,
757 nested: bool,
758) -> Bucket<'a> {
759 match *component {
760 Component::Root => Bucket::Root,
761 Component::ID(ref id) => {
762 if quirks_mode == QuirksMode::Quirks {
763 *bucket_matches = BucketMatches::Unknown;
765 }
766 Bucket::ID(id)
767 },
768 Component::Class(ref class) => {
769 if quirks_mode == QuirksMode::Quirks {
770 *bucket_matches = BucketMatches::Unknown;
772 }
773 Bucket::Class(class)
774 },
775 Component::AttributeInNoNamespace { ref local_name, .. } => {
776 *bucket_matches = BucketMatches::Unknown;
778 Bucket::Attribute {
779 name: local_name,
780 lower_name: local_name,
781 }
782 },
783 Component::AttributeInNoNamespaceExists {
784 ref local_name,
785 ref local_name_lower,
786 } => {
787 *bucket_matches = BucketMatches::Unknown;
789 Bucket::Attribute {
790 name: local_name,
791 lower_name: local_name_lower,
792 }
793 },
794 Component::AttributeOther(ref selector) => {
795 *bucket_matches = BucketMatches::Unknown;
797 Bucket::Attribute {
798 name: &selector.local_name,
799 lower_name: &selector.local_name_lower,
800 }
801 },
802 Component::LocalName(ref selector) => {
803 if selector.name != selector.lower_name {
804 *bucket_matches = BucketMatches::Unknown;
805 }
806 Bucket::LocalName {
807 name: &selector.name,
808 lower_name: &selector.lower_name,
809 }
810 },
811 Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {
812 Bucket::Namespace(url)
813 },
814 Component::Slotted(ref selector) => {
833 *bucket_matches = BucketMatches::Unknown;
836 find_bucket(
837 selector.iter(),
838 quirks_mode,
839 disjoint_buckets,
840 bucket_matches,
841 true,
842 )
843 },
844 Component::Host(ref selector) => {
845 *bucket_matches = BucketMatches::Unknown;
851 if let Some(selector) = selector {
852 find_bucket(
853 selector.iter(),
854 quirks_mode,
855 disjoint_buckets,
856 bucket_matches,
857 true,
858 )
859 } else {
860 Bucket::Universal
861 }
862 },
863 Component::Is(ref list) | Component::Where(ref list) => {
864 if list.len() == 1 {
865 find_bucket(
866 list.slice()[0].iter(),
867 quirks_mode,
868 disjoint_buckets,
869 bucket_matches,
870 true,
871 )
872 } else {
873 *bucket_matches = BucketMatches::Unknown;
878 for selector in list.slice() {
879 let bucket = find_bucket(
880 selector.iter(),
881 quirks_mode,
882 disjoint_buckets,
883 bucket_matches,
884 true,
885 );
886 if disjoint_buckets.last() == Some(&bucket) {
887 continue;
891 }
892 disjoint_buckets.push(bucket);
893 }
894 Bucket::Universal
895 }
896 },
897 Component::NonTSPseudoClass(ref pseudo_class)
898 if pseudo_class
899 .state_flag()
900 .intersects(RARE_PSEUDO_CLASS_STATES) =>
901 {
902 *bucket_matches = BucketMatches::Unknown;
905 Bucket::RarePseudoClasses
906 },
907 Component::PseudoElement(ref pseudo) => {
908 if pseudo.has_argument() || nested {
912 *bucket_matches = BucketMatches::Unknown;
913 }
914 Bucket::Universal
915 },
916 Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => {
917 Bucket::Universal
919 },
920 _ => {
921 *bucket_matches = BucketMatches::Unknown;
922 Bucket::Universal
923 },
924 }
925}
926
927#[inline(always)]
936fn find_bucket<'a>(
937 mut iter: SelectorIter<'a, SelectorImpl>,
938 quirks_mode: QuirksMode,
939 disjoint_buckets: &mut DisjointBuckets<'a>,
940 bucket_matches: &mut BucketMatches,
941 nested: bool,
942) -> Bucket<'a> {
943 let mut current_bucket = Bucket::Universal;
944
945 loop {
946 for ss in &mut iter {
947 let new_bucket =
948 specific_bucket_for(ss, quirks_mode, disjoint_buckets, bucket_matches, nested);
949 if current_bucket != Bucket::Universal {
952 *bucket_matches = BucketMatches::Unknown;
954 }
955 if new_bucket.more_or_equally_specific_than(¤t_bucket) {
956 current_bucket = new_bucket;
957 }
958 }
959
960 match iter.next_sequence() {
963 None => break,
964 Some(Combinator::PseudoElement) => continue,
965 Some(..) => {
966 if *bucket_matches != BucketMatches::Unknown {
968 if nested {
969 *bucket_matches = BucketMatches::Unknown;
970 } else {
971 *bucket_matches = BucketMatches::Subject;
972 }
973 }
974 break;
975 },
976 }
977 }
978
979 current_bucket
980}
981
982#[derive(Clone, Debug, MallocSizeOf)]
984pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V>(PrecomputedHashMap<K, V>);
985
986impl<V> Default for MaybeCaseInsensitiveHashMap<Atom, V> {
987 #[inline]
988 fn default() -> Self {
989 MaybeCaseInsensitiveHashMap(PrecomputedHashMap::default())
990 }
991}
992
993impl<V> MaybeCaseInsensitiveHashMap<Atom, V> {
994 pub fn new() -> Self {
996 Self::default()
997 }
998
999 pub fn shrink_if_needed(&mut self) {
1001 self.0.shrink_if_needed()
1002 }
1003
1004 pub fn try_entry(
1006 &mut self,
1007 mut key: Atom,
1008 quirks_mode: QuirksMode,
1009 ) -> Result<hash_map::Entry<'_, Atom, V>, AllocErr> {
1010 if quirks_mode == QuirksMode::Quirks {
1011 key = key.to_ascii_lowercase()
1012 }
1013 self.0.try_reserve(1)?;
1014 Ok(self.0.entry(key))
1015 }
1016
1017 #[inline]
1019 pub fn is_empty(&self) -> bool {
1020 self.0.is_empty()
1021 }
1022
1023 pub fn iter(&self) -> hash_map::Iter<'_, Atom, V> {
1025 self.0.iter()
1026 }
1027
1028 pub fn clear(&mut self) {
1030 self.0.clear()
1031 }
1032
1033 pub fn get(&self, key: &WeakAtom, quirks_mode: QuirksMode) -> Option<&V> {
1035 if quirks_mode == QuirksMode::Quirks {
1036 self.0.get(&key.to_ascii_lowercase())
1037 } else {
1038 self.0.get(key)
1039 }
1040 }
1041}
1042
1043#[test]
1044fn test_precomputed_hash_set() {
1045 let mut set = PrecomputedHashSet::default();
1046 let atom = crate::Atom::from("");
1047 assert!(!set.contains(&atom));
1048 set.insert(atom.clone());
1049 assert!(set.contains(&atom));
1050}