#[inline(always)]
const fn byte_eq(a: u8, b: u8, ignore_case: bool) -> bool {
if ignore_case {
a.eq_ignore_ascii_case(&b)
} else {
a == b
}
}
#[inline(always)]
const fn in_range(ch: u8, low: u8, high: u8, ignore_case: bool) -> bool {
let (low, high) = if low <= high {
(low, high)
} else {
(high, low)
};
if ignore_case {
let ch = ch.to_ascii_lowercase();
let low = low.to_ascii_lowercase();
let high = high.to_ascii_lowercase();
ch >= low && ch <= high
} else {
ch.wrapping_sub(low) <= high - low
}
}
#[inline]
const fn match_bracket(pat: &[u8], target_byte: u8, ignore_case: bool) -> (bool, usize) {
let mut i = 1;
let mut invert = false;
if i < pat.len() && pat[i] == b'^' {
invert = true;
i += 1;
}
let mut matched = false;
while i < pat.len() && pat[i] != b']' {
if pat[i] == b'\\' && i + 1 < pat.len() {
if !matched && pat[i + 1] == target_byte {
matched = true;
}
i += 2;
} else if pat.len() - i >= 3 && pat[i + 1] == b'-' {
let start = pat[i];
let end = pat[i + 2];
if !matched && in_range(target_byte, start, end, ignore_case) {
matched = true;
}
i += 3;
} else {
if !matched && byte_eq(pat[i], target_byte, ignore_case) {
matched = true;
}
i += 1;
}
}
if i >= pat.len() {
return (matched != invert, pat.len());
}
(matched != invert, i + 1)
}
#[inline]
pub const fn glob_match_opt(pattern: &[u8], target: &[u8], ignore_case: bool) -> bool {
if target.is_empty() {
return pattern.is_empty();
}
let mut p = 0;
let mut t = 0;
let mut star_p: Option<usize> = None;
let mut star_t = 0;
while t < target.len() {
if p < pattern.len() {
match pattern[p] {
b'*' => {
while p + 1 < pattern.len() && pattern[p + 1] == b'*' {
p += 1;
}
if p + 1 == pattern.len() {
return true;
}
star_p = Some(p);
p += 1;
star_t = t;
continue;
}
b'?' => {
p += 1;
t += 1;
continue;
}
b'[' => {
let (matched, consumed) = match_bracket(pattern.split_at(p).1, target[t], ignore_case);
if matched {
p += consumed;
t += 1;
continue;
}
}
b'\\' => {
let pat_byte = if p + 1 < pattern.len() {
pattern[p + 1]
} else {
b'\\'
};
if byte_eq(pat_byte, target[t], ignore_case) {
p += if p + 1 < pattern.len() { 2 } else { 1 };
t += 1;
continue;
}
}
c => {
if byte_eq(c, target[t], ignore_case) {
p += 1;
t += 1;
continue;
}
}
}
}
if let Some(sp) = star_p {
p = sp + 1;
star_t += 1;
t = star_t;
} else {
return false;
}
}
while p < pattern.len() && pattern[p] == b'*' {
p += 1;
}
p == pattern.len()
}
#[inline(always)]
pub const fn glob_match(pattern: &[u8], target: &[u8]) -> bool {
glob_match_opt(pattern, target, false)
}
#[inline(always)]
pub const fn glob_match_nocase(pattern: &[u8], target: &[u8]) -> bool {
glob_match_opt(pattern, target, true)
}