use std::ffi::{c_char, c_void};
use std::sync::Arc;
use vectorscan_rs_sys as hs;
#[cfg(target_os = "macos")]
use crate::vectorscan::allocator;
use crate::vectorscan::database::Database;
use crate::vectorscan::error::{AsResult, Error};
use crate::vectorscan::scratch::Scratch;
#[derive(Debug, Clone)]
pub struct VectorscanScanner {
db: Arc<Database>,
}
impl VectorscanScanner {
pub fn new(db: Arc<Database>) -> Result<Self, Error> {
#[cfg(target_os = "macos")]
allocator::init_allocator();
Ok(Self { db })
}
pub fn new_literal(patterns: &[&str], flags: &[u32]) -> Result<Self, Error> {
let db = Arc::new(Database::new_literal(patterns, flags)?);
Self::new(db)
}
#[inline(always)]
pub fn as_db_ptr(&self) -> *mut hs::hs_database_t {
self.db.as_ptr()
}
pub fn scan<F>(&self, haystack: &[u8], on_match: F) -> Result<(), Error>
where
F: FnMut(usize) -> bool,
{
let mut scratch = unsafe { Scratch::new(self.db.as_ptr())? };
self.scan_with_scratch(haystack, &mut scratch, on_match)
}
pub fn scan_with_scratch<F>(
&self,
haystack: &[u8],
scratch: &mut Scratch,
mut on_match: F,
) -> Result<(), Error>
where
F: FnMut(usize) -> bool,
{
let ctx = &mut on_match as *mut F as *mut c_void;
unsafe {
let status = hs::hs_scan(
self.db.as_ptr(),
haystack.as_ptr() as *const c_char,
haystack.len() as u32,
0,
scratch.as_ptr(),
Some(match_callback::<F>),
ctx,
);
match status {
s if s == hs::HS_SUCCESS as i32 || s == hs::HS_SCAN_TERMINATED => Ok(()),
_ => status.ok(),
}
}
}
}
extern "C" fn match_callback<F>(id: u32, _from: u64, _to: u64, _flags: u32, ctx: *mut c_void) -> i32
where
F: FnMut(usize) -> bool,
{
let on_match = unsafe { &mut *(ctx as *mut F) };
if on_match(id as usize) { 0 } else { 1 }
}