regex_automata/meta/
prefix.rs1use regex_syntax::hir::{literal::Literal, Class, Hir, HirKind};
13
14pub(super) fn hir_can_contain_literal(hir: &Hir, lit: &[u8]) -> bool {
22 lit.is_empty() || lit.iter().all(|&byte| hir_can_consume_byte(hir, byte))
23}
24
25pub(super) fn hir_has_fixed_length(hir: &Hir) -> bool {
30 let props = hir.properties();
31 props
32 .minimum_len()
33 .and_then(|min| props.maximum_len().map(|max| min == max))
34 .unwrap_or(false)
35}
36
37pub(super) fn has_disjoint_class_separator(
80 prefix: &Hir,
81 literals: &[Literal],
82) -> bool {
83 let hirs = match uncapture(prefix).kind() {
84 HirKind::Concat(hirs) if hirs.len() >= 2 => hirs,
85 _ => return false,
86 };
87 let Some(first) = hirs.iter().position(|hir| !hir_matches_empty_only(hir))
88 else {
89 return false;
90 };
91 let last =
92 hirs.iter().rposition(|hir| !hir_matches_empty_only(hir)).unwrap();
93 has_disjoint_class_separator_at(hirs, last, literals)
94 || (first != last
95 && has_disjoint_class_separator_at(hirs, first, literals))
96}
97
98fn has_disjoint_class_separator_at(
99 hirs: &[Hir],
100 separator: usize,
101 literals: &[Literal],
102) -> bool {
103 let Some(separator_class) = required_class(&hirs[separator]) else {
104 return false;
105 };
106 class_is_disjoint_from_literals(separator_class, literals)
107 && hirs.iter().enumerate().all(|(i, hir)| {
108 i == separator || hir_is_disjoint_from_class(hir, separator_class)
109 })
110}
111
112fn hir_matches_empty_only(hir: &Hir) -> bool {
113 hir.properties().maximum_len() == Some(0)
114}
115
116fn required_class(hir: &Hir) -> Option<&Class> {
117 let hir = uncapture(hir);
118 match hir.kind() {
119 HirKind::Class(cls) => Some(cls),
120 HirKind::Repetition(rep) if rep.min > 0 => {
121 match uncapture(&rep.sub).kind() {
122 HirKind::Class(cls) => Some(cls),
123 _ => None,
124 }
125 }
126 _ => None,
127 }
128}
129
130fn hir_is_disjoint_from_class(hir: &Hir, separator: &Class) -> bool {
131 match hir.kind() {
132 HirKind::Empty | HirKind::Look(_) => true,
133 HirKind::Literal(lit) => {
134 class_is_disjoint_from_literal(separator, &lit.0)
135 }
136 HirKind::Class(cls) => classes_are_disjoint(cls, separator),
137 HirKind::Repetition(rep) => {
138 hir_is_disjoint_from_class(&rep.sub, separator)
139 }
140 HirKind::Capture(capture) => {
141 hir_is_disjoint_from_class(&capture.sub, separator)
142 }
143 HirKind::Concat(hirs) | HirKind::Alternation(hirs) => {
144 hirs.iter().all(|hir| hir_is_disjoint_from_class(hir, separator))
145 }
146 }
147}
148
149fn uncapture(mut hir: &Hir) -> &Hir {
150 while let HirKind::Capture(capture) = hir.kind() {
151 hir = &capture.sub;
152 }
153 hir
154}
155
156fn classes_are_disjoint(left: &Class, right: &Class) -> bool {
157 match (left, right) {
158 (Class::Bytes(left), Class::Bytes(right)) => {
159 let (mut intersection, other) =
160 if left.ranges().len() <= right.ranges().len() {
161 (left.clone(), right)
162 } else {
163 (right.clone(), left)
164 };
165 intersection.intersect(other);
166 intersection.ranges().is_empty()
167 }
168 (Class::Unicode(left), Class::Unicode(right)) => {
169 let (mut intersection, other) =
170 if left.ranges().len() <= right.ranges().len() {
171 (left.clone(), right)
172 } else {
173 (right.clone(), left)
174 };
175 intersection.intersect(other);
176 intersection.ranges().is_empty()
177 }
178 _ => false,
179 }
180}
181
182fn class_is_disjoint_from_literals(cls: &Class, literals: &[Literal]) -> bool {
183 literals
184 .iter()
185 .all(|lit| class_is_disjoint_from_literal(cls, lit.as_bytes()))
186}
187
188fn class_is_disjoint_from_literal(cls: &Class, lit: &[u8]) -> bool {
189 match cls {
190 Class::Bytes(cls) => {
191 lit.iter().all(|&byte| !byte_class_contains(cls, byte))
192 }
193 Class::Unicode(cls) => core::str::from_utf8(lit)
194 .map_or(false, |lit| {
195 lit.chars().all(|ch| !unicode_class_contains(cls, ch))
196 }),
197 }
198}
199
200fn hir_can_consume_byte(hir: &Hir, byte: u8) -> bool {
201 match hir.kind() {
202 HirKind::Empty | HirKind::Look(_) => false,
203 HirKind::Literal(lit) => lit.0.contains(&byte),
204 HirKind::Class(Class::Bytes(cls)) => byte_class_contains(cls, byte),
205 HirKind::Class(Class::Unicode(cls)) => {
206 if byte > 0x7F {
212 return true;
213 }
214 let ch = char::from(byte);
215 unicode_class_contains(cls, ch)
216 }
217 HirKind::Repetition(rep) => hir_can_consume_byte(&rep.sub, byte),
218 HirKind::Capture(capture) => hir_can_consume_byte(&capture.sub, byte),
219 HirKind::Concat(hirs) | HirKind::Alternation(hirs) => {
220 hirs.iter().any(|hir| hir_can_consume_byte(hir, byte))
221 }
222 }
223}
224
225fn byte_class_contains(cls: ®ex_syntax::hir::ClassBytes, byte: u8) -> bool {
226 cls.ranges()
227 .iter()
228 .any(|range| range.start() <= byte && byte <= range.end())
229}
230
231fn unicode_class_contains(
232 cls: ®ex_syntax::hir::ClassUnicode,
233 ch: char,
234) -> bool {
235 cls.ranges().iter().any(|range| range.start() <= ch && ch <= range.end())
236}