use dynasmrt::ExecutableBuffer;
use super::OnePass;
#[cfg(target_arch = "x86_64")]
pub(super) type MatchFn = unsafe extern "sysv64" fn(*const u8, usize, usize, *mut i64) -> i64;
#[cfg(target_arch = "aarch64")]
pub(super) type MatchFn = unsafe extern "C" fn(*const u8, usize, usize, *mut i64) -> i64;
pub(super) struct Compiled {
#[allow(dead_code)]
pub(super) code: ExecutableBuffer,
pub(super) run: MatchFn,
}
pub struct OnePassJit {
compiled: Compiled,
slot_count: usize,
}
impl std::fmt::Debug for OnePassJit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OnePassJit")
.field("slot_count", &self.slot_count)
.finish()
}
}
unsafe impl Send for OnePassJit {}
unsafe impl Sync for OnePassJit {}
impl OnePassJit {
pub fn compile(one_pass: &OnePass) -> Option<Self> {
#[cfg(target_arch = "x86_64")]
{
if !super::x86_64::is_supported(one_pass) {
return None;
}
Some(Self {
compiled: super::x86_64::compile(one_pass)?,
slot_count: one_pass.slot_count,
})
}
#[cfg(target_arch = "aarch64")]
{
if !super::aarch64::is_supported(one_pass) {
return None;
}
Some(Self {
compiled: super::aarch64::compile(one_pass)?,
slot_count: one_pass.slot_count,
})
}
}
pub fn slot_count(&self) -> usize {
self.slot_count
}
pub fn captures_at_into(
&self,
input: &[u8],
start: usize,
slots: &mut [Option<(usize, usize)>],
) -> bool {
if start > input.len() || slots.len() != self.slot_count {
return false;
}
let mut raw = [-1i64; RAW_SLOTS];
let Some(raw) = raw.get_mut(..self.slot_count * 2) else {
return false;
};
let end =
unsafe { (self.compiled.run)(input.as_ptr(), input.len(), start, raw.as_mut_ptr()) };
if end < 0 {
return false;
}
for (slot, pair) in slots.iter_mut().zip(raw.chunks_exact(2)) {
*slot = match (pair.first(), pair.get(1)) {
(Some(&group_start), Some(&group_end)) if group_start >= 0 && group_end >= 0 => {
Some((group_start as usize, group_end as usize))
}
_ => None,
};
}
true
}
}
const RAW_SLOTS: usize = 64;