#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]
mod ffi;
extern crate alloc;
use alloc::boxed::Box;
use core::str;
use regex_syntax::hir::{Class, Hir, HirKind};
use regex_syntax::Parser;
const WEIGHT: u64 = 1 << SHIFT;
const STEP: u64 = 128;
const BITS_U64: u32 = 64;
const SHIFT: u32 = 16;
const MAX_SHIFT: u32 = SHIFT - 1;
const LOOK_SHIFT: u32 = SHIFT - (64 - STEP.leading_zeros() - 1);
pub fn get(string: &str, pattern: &str) -> Result<u64, Box<regex_syntax::Error>> {
let hir = Parser::new().parse(pattern).map_err(Box::new)?;
let bytes = string.as_bytes();
let (result, _) = calc(&hir, bytes, WEIGHT);
Ok(result)
}
fn calc(hir: &Hir, bytes: &[u8], weight: u64) -> (u64, usize) {
match hir.kind() {
HirKind::Empty => (0, 0),
HirKind::Literal(literal) => {
let len = literal.0.len().min(bytes.len());
(len as u64 * weight, len)
}
HirKind::Class(class) => {
if bytes.is_empty() {
return (0, 0);
}
let len = if let Class::Unicode(_) = class {
str::from_utf8(bytes)
.ok()
.and_then(|s| s.chars().next())
.map(|c| c.len_utf8())
.unwrap_or(1)
} else {
1
};
let size: u64 = match class {
Class::Unicode(u) => u.ranges().iter().map(|r| r.len() as u64).sum(),
Class::Bytes(b) => b.ranges().iter().map(|r| r.len() as u64).sum(),
};
let result = if size <= 1 {
weight
} else {
let shift = (BITS_U64 - size.leading_zeros()).min(MAX_SHIFT);
(weight >> shift).max(1)
};
(result, len)
}
HirKind::Look(_) => ((weight >> LOOK_SHIFT).max(1), 0),
HirKind::Repetition(repetition) => {
let mut result = 0;
let mut offset = 0;
let mut weight = weight;
while offset < bytes.len() {
let (r, i) = calc(&repetition.sub, &bytes[offset..], weight);
if i == 0 {
break;
}
result += r;
offset += i;
weight = weight.saturating_sub(i as u64 * STEP);
}
(result, offset)
}
HirKind::Capture(capture) => calc(&capture.sub, bytes, weight),
HirKind::Concat(concat) => {
let mut result = 0;
let mut offset = 0;
let mut weight = weight;
for hir in concat {
let bytes = if offset < bytes.len() {
&bytes[offset..]
} else {
&[]
};
let (r, i) = calc(hir, bytes, weight);
result += r;
offset += i;
weight = weight.saturating_sub(i as u64 * STEP);
}
(result, offset)
}
HirKind::Alternation(alternation) => {
for hir in alternation {
let (r, i) = calc(hir, bytes, weight);
if i > 0 || matches!(hir.kind(), HirKind::Empty) {
return (r / (alternation.len() as u64).max(1), i);
}
}
(0, 0)
}
}
}