pub mod cidr;
pub(crate) mod classify;
pub mod codetag;
pub mod color;
pub mod datetime;
pub mod domain;
pub mod email;
pub mod emoji;
pub mod env;
pub mod handle;
pub mod hash;
pub(crate) mod iana;
pub mod ip;
pub(crate) mod ipv6;
pub mod json;
pub mod jwt;
pub mod mac;
pub mod mirror;
pub mod modeline;
pub mod path;
pub mod phone;
pub(crate) mod registry;
pub mod scanner;
pub(crate) mod search;
pub mod semver;
pub mod uri;
pub mod uuid;
pub(crate) mod word;
use std::ops::Range;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ByteSet([u64; 4]);
impl ByteSet {
pub const EMPTY: ByteSet = ByteSet([0; 4]);
pub const ALL: ByteSet = ByteSet([u64::MAX; 4]);
pub const fn from_bytes(bytes: &[u8]) -> ByteSet {
let mut set = ByteSet::EMPTY;
let mut i = 0;
while i < bytes.len() {
set = set.with(bytes[i]);
i += 1;
}
set
}
pub fn from_fn(f: impl Fn(u8) -> bool) -> ByteSet {
let mut set = ByteSet::EMPTY;
for b in 0..=255u8 {
if f(b) {
set = set.with(b);
}
}
set
}
pub const fn with(mut self, byte: u8) -> ByteSet {
self.0[(byte >> 6) as usize] |= 1u64 << (byte & 63);
self
}
#[inline]
pub const fn contains(&self, byte: u8) -> bool {
self.0[(byte >> 6) as usize] & (1u64 << (byte & 63)) != 0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum RunClass {
Digit,
Hex,
Word,
}
impl RunClass {
#[inline(always)]
pub fn contains(self, b: u8) -> bool {
match self {
RunClass::Digit => b.is_ascii_digit(),
RunClass::Hex => b.is_ascii_hexdigit(),
RunClass::Word => b.is_ascii_alphanumeric() || b == b'_',
}
}
}
pub const RUN_CAP: u8 = 129;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Run {
pub len: u8,
pub capped: bool,
pub after: Option<u8>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Runs {
pub digit: Run,
pub hex: Run,
pub word: Run,
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct RunCache {
start: usize,
end: usize,
open: bool,
}
pub(crate) struct RunHint<'a> {
pub(crate) hex: &'a [u64],
pub(crate) digit: &'a [u64],
pub(crate) lane: usize,
pub(crate) stride: u32,
}
impl RunHint<'_> {
#[inline(always)]
pub(crate) fn run(&self, masks: &[u64]) -> Option<usize> {
let lane_bits = |m: u64, lane: usize| {
let shifted = m >> (lane as u32 * self.stride);
(((!shifted).trailing_zeros() / self.stride) as usize).min(BLOCK_LANES - lane)
};
let mut lane = self.lane;
let mut total = 0;
for &m in masks {
let run = lane_bits(m, lane);
total += run;
if run < BLOCK_LANES - lane {
return Some(total);
}
lane = 0;
}
None
}
}
const BLOCK_LANES: usize = 16;
impl Runs {
pub fn at(input: &[u8], pos: usize) -> Runs {
let mut runs = Self::at_cached(input, pos, &mut RunCache::default(), RUN_CAP as usize);
runs.measure_word(input, pos, RUN_CAP as usize);
runs
}
#[inline(always)]
pub(crate) fn measure_word(&mut self, input: &[u8], pos: usize, cap: usize) {
self.word = Self::word_at(input, pos, cap);
}
#[inline(always)]
pub(crate) fn word_at(input: &[u8], pos: usize, cap: usize) -> Run {
let limit = input.len().min(pos + cap);
let mut end = pos;
while end < limit && RunClass::Word.contains(input[end]) {
end += 1;
}
Run {
len: (end - pos) as u8,
capped: end - pos >= cap,
after: input.get(end).copied(),
}
}
#[inline(always)]
pub(crate) fn only_word(word: Run) -> Runs {
Runs {
digit: UNMEASURED,
hex: UNMEASURED,
word,
}
}
#[inline(always)]
pub(crate) fn at_hinted(
input: &[u8],
pos: usize,
hint: &RunHint<'_>,
cache: &mut RunCache,
cap: usize,
) -> Runs {
if cache.start <= pos && pos < cache.end && (!cache.open || cache.end - pos > cap) {
return Self::at_cached(input, pos, cache, cap);
}
let hex_len = match hint.run(hint.hex) {
Some(len) => len.min(cap),
None => {
let limit = input.len().min(pos + cap);
let mut hex = pos + hint.hex.len() * BLOCK_LANES - hint.lane;
while hex < limit && input[hex].is_ascii_hexdigit() {
hex += 1;
}
hex.min(limit) - pos
}
};
let hex_end = pos + hex_len;
*cache = RunCache {
start: pos,
end: hex_end,
open: hex_len >= cap && hex_end < input.len(),
};
let digit_len = match hint.run(hint.digit) {
Some(len) => len.min(hex_len),
None => {
let mut digit = pos + hint.digit.len() * BLOCK_LANES - hint.lane;
while digit < hex_end && input[digit].is_ascii_digit() {
digit += 1;
}
digit.min(hex_end) - pos
}
};
let run = |len: usize| Run {
len: len as u8,
capped: len >= cap,
after: input.get(pos + len).copied(),
};
Runs {
digit: run(digit_len),
hex: run(hex_len),
word: UNMEASURED,
}
}
#[inline(always)]
pub(crate) fn at_cached(input: &[u8], pos: usize, cache: &mut RunCache, cap: usize) -> Runs {
let hex_end =
if cache.start <= pos && pos < cache.end && (!cache.open || cache.end - pos > cap) {
cache.end.min(pos + cap)
} else {
let limit = input.len().min(pos + cap);
let mut hex = pos;
while hex < limit && input[hex].is_ascii_hexdigit() {
hex += 1;
}
*cache = RunCache {
start: pos,
end: hex,
open: hex == limit && hex < input.len(),
};
hex
};
let mut digit = pos;
while digit < hex_end && input[digit].is_ascii_digit() {
digit += 1;
}
let run = |stop: usize| Run {
len: (stop - pos) as u8,
capped: stop - pos >= cap,
after: input.get(stop).copied(),
};
Runs {
digit: run(digit),
hex: run(hex_end),
word: UNMEASURED,
}
}
#[inline(always)]
fn get(&self, class: RunClass) -> &Run {
[&self.digit, &self.hex, &self.word][class as usize]
}
}
const UNMEASURED: Run = Run {
len: RUN_CAP,
capped: true,
after: None,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Then {
pub class: RunClass,
pub min: u8,
pub max: u8,
pub after: ByteSet,
pub after_end: bool,
}
pub const MAX_THEN: usize = 2;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RunRule {
pub cur: ByteSet,
pub class: RunClass,
pub min: u8,
pub max: u8,
pub lengths: u64,
pub after: ByteSet,
pub after_end: bool,
pub then: [Option<Then>; MAX_THEN],
}
impl RunRule {
pub fn new(class: RunClass, min: u8, max: u8) -> RunRule {
let cur = ByteSet::from_fn(|b| class.contains(b));
RunRule {
cur,
class,
min,
max,
lengths: 0,
after: ByteSet::ALL,
after_end: true,
then: [None; MAX_THEN],
}
}
pub fn lengths(mut self, lengths: u64) -> RunRule {
self.lengths = lengths;
self
}
pub fn followed_by(self, bytes: &[u8]) -> RunRule {
self.followed_by_set(ByteSet::from_bytes(bytes))
}
pub fn followed_by_set(mut self, set: ByteSet) -> RunRule {
match self.then.iter_mut().rev().find_map(Option::as_mut) {
Some(step) => {
step.after = set;
step.after_end = false;
}
None => {
self.after = set;
self.after_end = false;
}
}
self
}
pub fn or_end(mut self) -> RunRule {
match self.then.iter_mut().rev().find_map(Option::as_mut) {
Some(step) => step.after_end = true,
None => self.after_end = true,
}
self
}
pub fn then(mut self, class: RunClass, min: u8, max: u8) -> RunRule {
let slot = self
.then
.iter_mut()
.find(|slot| slot.is_none())
.expect("a rule chains at most MAX_THEN steps");
*slot = Some(Then {
class,
min,
max,
after: ByteSet::ALL,
after_end: true,
});
self
}
#[inline]
fn applies(&self, cur: u8) -> bool {
self.cur.contains(cur)
}
#[inline]
fn accepts(&self, runs: &Runs, input: &[u8], pos: usize) -> bool {
let run = runs.get(self.class);
if run.len < self.min || run.len > self.max {
return false;
}
if run.capped {
return true;
}
if self.lengths != 0 && run.len < 64 && self.lengths & (1u64 << run.len) == 0 {
return false;
}
match run.after {
Some(b) if self.after.contains(b) => {}
Some(_) => return false,
None => return self.after_end,
}
let mut at = pos + run.len as usize + 1;
for step in self.then.iter().flatten() {
let limit = input.len().min(at + step.max as usize + 1);
let mut end = at;
while end < limit && step.class.contains(input[end]) {
end += 1;
}
let len = end - at;
if len > step.max as usize && step.max >= RUN_CAP {
return true;
}
if len < step.min as usize || len > step.max as usize {
return false;
}
match input.get(end) {
Some(&b) if step.after.contains(b) => {}
Some(_) => return false,
None => return step.after_end,
}
at = end + 1;
}
true
}
#[inline(always)]
pub fn allow(rules: &[RunRule], cur: u8, runs: &Runs, input: &[u8], pos: usize) -> bool {
let mut applicable = false;
for rule in rules {
if rule.applies(cur) {
if rule.accepts(runs, input, pos) {
return true;
}
applicable = true;
}
}
!applicable
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Memo {
pub start: usize,
pub end: usize,
pub aux: usize,
}
impl Memo {
#[inline]
pub fn covers(&self, pos: usize) -> bool {
self.start <= pos && pos < self.end
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Anchor {
pub bytes: ByteSet,
pub walk: ByteSet,
pub checks: [Option<AnchorCheck>; MAX_CHECKS],
pub back: Option<u8>,
}
pub const MAX_CHECK_OFFSETS: usize = 5;
pub const MAX_CHECKS: usize = 2;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AnchorCheck {
pub anchors: ByteSet,
pub offsets: [u8; MAX_CHECK_OFFSETS],
pub offset_count: u8,
pub bytes: ByteSet,
pub all: bool,
}
impl AnchorCheck {
pub fn offsets(&self) -> &[u8] {
&self.offsets[..self.offset_count as usize]
}
#[inline]
pub fn passes(&self, input: &[u8], pos: usize) -> bool {
let seen = |&offset: &u8| {
input
.get(pos + offset as usize)
.is_some_and(|&b| self.bytes.contains(b))
};
if self.all {
self.offsets().iter().all(seen)
} else {
self.offsets().iter().any(seen)
}
}
}
impl Anchor {
pub fn new(bytes: ByteSet, walk: ByteSet) -> Anchor {
Anchor {
bytes,
walk,
checks: [None; MAX_CHECKS],
back: None,
}
}
pub fn back(mut self, back: u8) -> Anchor {
self.back = Some(back);
self
}
pub fn confirm(self, anchors: &[u8], offsets: &[u8], bytes: &[u8]) -> Anchor {
self.check(anchors, offsets, bytes, false)
}
pub fn confirm_all(self, anchors: &[u8], offsets: &[u8], bytes: &[u8]) -> Anchor {
self.check(anchors, offsets, bytes, true)
}
fn check(mut self, anchors: &[u8], offsets: &[u8], bytes: &[u8], all: bool) -> Anchor {
assert!(
!offsets.is_empty() && offsets.len() <= MAX_CHECK_OFFSETS,
"an anchor check lists one to {MAX_CHECK_OFFSETS} offsets"
);
let mut list = [0u8; MAX_CHECK_OFFSETS];
list[..offsets.len()].copy_from_slice(offsets);
let slot = self
.checks
.iter_mut()
.find(|slot| slot.is_none())
.expect("an anchor carries at most MAX_CHECKS checks");
*slot = Some(AnchorCheck {
anchors: ByteSet::from_bytes(anchors),
offsets: list,
offset_count: offsets.len() as u8,
bytes: ByteSet::from_bytes(bytes),
all,
});
self
}
}
pub trait Finder: Send + Sync {
fn id(&self) -> &'static str;
fn find(&self, s: &str) -> Option<Range<usize>>;
fn dispatchable(&self) -> bool {
false
}
fn could_start_at(&self, _byte: u8) -> bool {
true
}
fn try_at(&self, _input: &[u8], _pos: usize) -> Option<Range<usize>> {
None
}
fn could_start_after(&self, _prev: u8, _cur: u8) -> bool {
true
}
fn could_continue_with(&self, _cur: u8, _next: u8) -> bool {
true
}
fn try_at_memo(&self, input: &[u8], pos: usize, memo: &mut Memo) -> Option<Range<usize>> {
let _ = memo;
self.try_at(input, pos)
}
fn try_trigger_at_memo(
&self,
input: &[u8],
pos: usize,
memo: &mut Memo,
) -> Option<Range<usize>> {
let _ = memo;
self.try_trigger_at(input, pos)
}
fn line_agnostic(&self) -> bool {
false
}
fn anchor(&self) -> Option<Anchor> {
None
}
fn prefixes(&self) -> Vec<Vec<u8>> {
Vec::new()
}
fn run_rules(&self) -> Vec<RunRule> {
Vec::new()
}
fn triggerable(&self) -> bool {
false
}
fn could_trigger_at(&self, _byte: u8) -> bool {
false
}
fn has_trigger_context(&self) -> bool {
false
}
fn trigger_context(&self, _prev2: u8, _prev1: u8) -> bool {
true
}
fn trigger_context_exempt(&self, _next: Option<u8>) -> bool {
false
}
fn try_trigger_at(&self, _input: &[u8], _pos: usize) -> Option<Range<usize>> {
None
}
}