use crate::hir::{Hir, HirExpr};
use crate::nfa::Nfa;
use crate::vm::{is_shift_or_compatible, is_shift_or_wide_compatible};
fn hir_uses_codepoint_class(expr: &HirExpr) -> bool {
match expr {
HirExpr::UnicodeCpClass(_) => true,
HirExpr::Concat(exprs) | HirExpr::Alt(exprs) => exprs.iter().any(hir_uses_codepoint_class),
HirExpr::Repeat(r) => hir_uses_codepoint_class(&r.expr),
HirExpr::Capture(c) => hir_uses_codepoint_class(&c.expr),
HirExpr::Lookaround(l) => hir_uses_codepoint_class(&l.expr),
HirExpr::Empty
| HirExpr::Literal(_)
| HirExpr::Class(_)
| HirExpr::Anchor(_)
| HirExpr::Backref(_) => false,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EngineType {
PikeVm,
BacktrackingVm,
ShiftOr,
ShiftOrWide,
LazyDfa,
#[cfg(feature = "jit")]
Jit,
}
pub fn select_engine(nfa: &Nfa) -> EngineType {
if nfa.has_backrefs {
return EngineType::BacktrackingVm;
}
if nfa.has_lookaround {
return EngineType::PikeVm;
}
EngineType::LazyDfa
}
pub fn select_engine_from_hir(hir: &Hir) -> EngineType {
if hir.props.has_backrefs {
return EngineType::BacktrackingVm;
}
if hir.props.has_lookaround || hir.props.has_non_greedy {
return EngineType::PikeVm;
}
if hir_uses_codepoint_class(&hir.expr) {
return EngineType::PikeVm;
}
if is_shift_or_compatible(hir) {
return EngineType::ShiftOr;
}
if hir.props.has_multiline_anchors {
return EngineType::LazyDfa;
}
if is_shift_or_wide_compatible(hir) {
return EngineType::ShiftOrWide;
}
if hir.props.has_word_boundary {
return EngineType::LazyDfa;
}
EngineType::LazyDfa
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Capabilities {
#[cfg(feature = "simd")]
pub has_avx2: bool,
#[cfg(feature = "jit")]
pub has_jit: bool,
}
impl Capabilities {
pub fn detect() -> Self {
Self {
#[cfg(feature = "simd")]
has_avx2: Self::detect_avx2(),
#[cfg(feature = "jit")]
has_jit: Self::detect_jit(),
}
}
#[cfg(feature = "simd")]
fn detect_avx2() -> bool {
#[cfg(target_arch = "x86_64")]
{
is_x86_feature_detected!("avx2")
}
#[cfg(not(target_arch = "x86_64"))]
{
false
}
}
#[cfg(feature = "jit")]
fn detect_jit() -> bool {
cfg!(any(target_arch = "x86_64", target_arch = "aarch64"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hir::translate;
use crate::nfa::compile;
use crate::parser::parse;
fn get_engine_from_hir(pattern: &str) -> EngineType {
let ast = parse(pattern).unwrap();
let hir = translate(&ast).unwrap();
select_engine_from_hir(&hir)
}
fn get_engine_from_nfa(pattern: &str) -> EngineType {
let ast = parse(pattern).unwrap();
let hir = translate(&ast).unwrap();
let nfa = compile(&hir).unwrap();
select_engine(&nfa)
}
#[test]
fn test_simple_pattern_uses_shift_or() {
let engine = get_engine_from_hir("abc");
assert_eq!(engine, EngineType::ShiftOr);
}
#[test]
fn test_medium_pattern_uses_shift_or_wide() {
let medium_pattern = "a".repeat(100);
let engine = get_engine_from_hir(&medium_pattern);
assert_eq!(engine, EngineType::ShiftOrWide);
}
#[test]
fn test_very_long_pattern_uses_lazy_dfa() {
let long_pattern = "a".repeat(300);
let engine = get_engine_from_hir(&long_pattern);
assert_eq!(engine, EngineType::LazyDfa);
}
#[test]
fn test_backref_uses_backtracking() {
let ast = parse(r"(a)\1").unwrap();
let hir = translate(&ast).unwrap();
let nfa = compile(&hir).unwrap();
if nfa.has_backrefs {
assert_eq!(select_engine(&nfa), EngineType::BacktrackingVm);
}
if hir.props.has_backrefs {
assert_eq!(select_engine_from_hir(&hir), EngineType::BacktrackingVm);
}
}
#[test]
fn test_nfa_api_defaults_to_lazy_dfa() {
let engine = get_engine_from_nfa("abc");
assert_eq!(engine, EngineType::LazyDfa);
}
#[test]
fn test_word_boundary_uses_lazy_dfa() {
assert_eq!(get_engine_from_hir(r"\bthe\b"), EngineType::LazyDfa);
assert_eq!(get_engine_from_hir(r"\bword\b"), EngineType::LazyDfa);
assert_eq!(get_engine_from_hir(r"\b\d+\b"), EngineType::LazyDfa);
assert_eq!(get_engine_from_hir(r"a\Bb"), EngineType::LazyDfa);
let long_pattern = format!(r"\b{}\b", "a".repeat(100));
assert_eq!(get_engine_from_hir(&long_pattern), EngineType::LazyDfa);
}
#[test]
fn test_anchors_engine_selection() {
assert_eq!(get_engine_from_hir(r"^hello"), EngineType::ShiftOr);
assert_eq!(get_engine_from_hir(r"world$"), EngineType::ShiftOr);
assert_eq!(get_engine_from_hir(r"^hello$"), EngineType::ShiftOr);
assert_eq!(get_engine_from_hir(r"(?m)^line"), EngineType::LazyDfa);
assert_eq!(get_engine_from_hir(r"(?m)line$"), EngineType::LazyDfa);
}
}