use super::parse::{Node, NodeKind, UNBOUNDED};
pub(crate) fn linear_cost(n: &Node) -> i64 {
match n.kind {
NodeKind::Empty => 0,
NodeKind::Alt if n.subs.iter().all(|sub| matches!(sub.kind, NodeKind::Char | NodeKind::Set)) => 1,
NodeKind::Cat | NodeKind::Alt | NodeKind::Group => {
let mut cost = n.subs.len() as i64 - 1;
for sub in &n.subs {
cost += linear_cost(sub);
if cost > MAX_RE2_COST {
return MAX_RE2_COST + 1;
}
}
cost
}
NodeKind::Repeat => {
let copies = (if n.max == UNBOUNDED { n.min } else { n.max }).max(1) as i64;
let body = linear_cost(&n.subs[0]) + 1;
if copies > MAX_RE2_COST || body * copies > MAX_RE2_COST {
return MAX_RE2_COST + 1;
}
body * copies
}
_ => 1,
}
}
const MAX_LINEAR_SIZE: i64 = 1 << 30;
pub(crate) fn linear_size(n: &Node) -> i64 {
const STATE: i64 = 96;
const WIDE_CLASS: i64 = 64;
const FIXED: i64 = 56;
fn states(n: &Node) -> i64 {
match n.kind {
NodeKind::Empty => 0,
NodeKind::Char => {
if n.r < 0x80 {
1
} else {
4
}
}
NodeKind::Set => {
let ranges = &n.class().ranges;
if ranges.last().is_none_or(|&last| last < 0x80) { 1 } else { WIDE_CLASS + ranges.len() as i64 / 2 }
}
NodeKind::Cat | NodeKind::Alt | NodeKind::Group => {
let mut total = n.subs.len() as i64;
for sub in &n.subs {
total += states(sub);
if total > MAX_LINEAR_SIZE {
return MAX_LINEAR_SIZE;
}
}
total
}
NodeKind::Repeat => {
let copies = (if n.max == UNBOUNDED { n.min } else { n.max }).max(1) as i64;
(copies * (states(&n.subs[0]) + 1)).min(MAX_LINEAR_SIZE)
}
_ => 1,
}
}
(STATE * (states(n) + FIXED)).min(MAX_LINEAR_SIZE)
}
pub(crate) const MAX_RE2_COST: i64 = 10_000;
pub(crate) fn re2_cost(n: &Node) -> i64 {
match n.kind {
NodeKind::Empty => 0,
NodeKind::Cat | NodeKind::Alt | NodeKind::Group => {
let mut cost = n.subs.len() as i64 - 1;
for sub in &n.subs {
cost += re2_cost(sub);
if cost > MAX_RE2_COST {
return MAX_RE2_COST + 1;
}
}
cost
}
NodeKind::Repeat => {
let mut copies = n.max as i64;
if n.max == UNBOUNDED {
copies = n.min as i64;
}
if copies < 1 {
copies = 1;
}
let body = re2_cost(&n.subs[0]) + 1;
if copies > MAX_RE2_COST || body * copies > MAX_RE2_COST {
return MAX_RE2_COST + 1;
}
body * copies
}
_ => 1,
}
}