#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
use core::ffi::CStr;
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
pub const DEFAULT_NODES: usize = 32;
pub const DEFAULT_CCL: usize = 64;
pub const DEFAULT_MATCH_TEXT_LEN: usize = 64;
pub const DEFAULT_MEMO: usize =
(DEFAULT_NODES * DEFAULT_MATCH_TEXT_LEN + 7) / 8;
pub struct RegexBuf<
const N: usize = DEFAULT_NODES,
const CCL: usize = DEFAULT_CCL,
const MEMO: usize = DEFAULT_MEMO,
> {
re_nodes: [regex_t; N],
ccl: [u8; CCL],
}
impl<const N: usize, const CCL: usize, const MEMO: usize> RegexBuf<N, CCL, MEMO> {
fn compile_into(re_nodes: &mut [regex_t; N], ccl: &mut [u8; CCL], pattern: &CStr) -> bool {
ccl.fill(0);
let bytes_written = unsafe {
re_compile(
re_nodes.as_mut_ptr() as *mut regex_t,
N,
ccl.as_mut_ptr(),
CCL,
pattern.as_ptr(),
)
};
bytes_written > 0
}
pub fn new(pattern: &CStr) -> Option<RegexBuf<N, CCL, MEMO>> {
let mut re_nodes: [regex_t; N] = unsafe { core::mem::zeroed() };
let mut ccl = [0u8; CCL];
if Self::compile_into(&mut re_nodes, &mut ccl, pattern) {
Some(RegexBuf { re_nodes, ccl })
} else {
None
}
}
pub fn recompile(mut self, pattern: &CStr) -> Option<RegexBuf<N, CCL, MEMO>> {
if Self::compile_into(&mut self.re_nodes, &mut self.ccl, pattern) {
Some(self)
} else {
None
}
}
pub(crate) fn matchp_at(
&self,
haystack: &CStr,
start: usize,
match_length: &mut i32,
) -> i32 {
if start > haystack.to_bytes().len() || start > i32::MAX as usize {
*match_length = 0;
return -1;
}
let sub = unsafe { CStr::from_ptr(haystack.as_ptr().add(start)) };
let mut len: i32 = 0;
let mut memo = [0u8; MEMO];
let memo_stride = if MEMO == 0 { 0 } else { MEMO * 8 / N };
let ret = unsafe {
re_matchp(
self.re_nodes.as_ptr() as re_t,
self.ccl.as_ptr(),
sub.as_ptr(),
&mut len,
memo.as_mut_ptr(),
MEMO,
memo_stride,
)
};
*match_length = len;
if ret < 0 { return -1; }
ret + start as i32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matchcharclass_correctness_depends_on_ccl_sentinel_byte() {
use core::ffi::CStr;
type R = RegexBuf;
let mut re = R::new(c"[-abc]").unwrap();
let mut ml = 0i32;
assert!(re.matchp_at(CStr::from_bytes_with_nul(b"-\0").unwrap(), 0, &mut ml) >= 0,
"dash should match as a literal when ccl[0] == 0");
re.ccl[0] = b'x';
let mut ml2 = 0i32;
assert!(re.matchp_at(CStr::from_bytes_with_nul(b"-\0").unwrap(), 0, &mut ml2) < 0,
"dash fails to match after sentinel byte is corrupted — proves str[-1] dependency");
}
}