use std::io::Read;
use std::mem;
use std::ptr;
use foreign_types::ForeignTypeRef;
use libc::{c_char, c_uint};
use crate::{
common::{Block, DatabaseRef, Streaming, Vectored},
error::AsResult,
ffi,
runtime::{split_closure, ScratchRef, StreamRef},
Result,
};
#[cfg(feature = "async")]
use futures::io::{AsyncRead, AsyncReadExt};
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Matching {
Continue = 0,
Terminate = 1,
}
impl Default for Matching {
fn default() -> Self {
Matching::Continue
}
}
pub trait MatchEventHandler {
unsafe fn split(&mut self) -> (ffi::match_event_handler, *mut libc::c_void);
}
impl MatchEventHandler for () {
unsafe fn split(&mut self) -> (ffi::match_event_handler, *mut libc::c_void) {
(None, ptr::null_mut())
}
}
impl MatchEventHandler for Matching {
unsafe fn split(&mut self) -> (ffi::match_event_handler, *mut libc::c_void) {
unsafe extern "C" fn trampoline(_: u32, _: u64, _: u64, _: u32, ctx: *mut ::libc::c_void) -> ::libc::c_int {
ctx.cast::<Matching>().read() as _
}
(Some(trampoline), self as *mut _ as *mut _)
}
}
impl MatchEventHandler for (ffi::match_event_handler, *mut libc::c_void) {
unsafe fn split(&mut self) -> (ffi::match_event_handler, *mut libc::c_void) {
*self
}
}
impl<F> MatchEventHandler for F
where
F: FnMut(u32, u64, u64, u32) -> Matching,
{
unsafe fn split(&mut self) -> (ffi::match_event_handler, *mut libc::c_void) {
let (callback, userdata) = split_closure(self);
(Some(mem::transmute(callback)), userdata)
}
}
impl DatabaseRef<Block> {
pub fn scan<T, F>(&self, data: T, scratch: &ScratchRef, mut on_match_event: F) -> Result<()>
where
T: AsRef<[u8]>,
F: MatchEventHandler,
{
let data = data.as_ref();
unsafe {
let (callback, userdata) = on_match_event.split();
ffi::hs_scan(
self.as_ptr(),
data.as_ptr() as *const c_char,
data.len() as u32,
0,
scratch.as_ptr(),
callback,
userdata,
)
.ok()
}
}
}
impl DatabaseRef<Vectored> {
pub fn scan<I, T, F>(&self, data: I, scratch: &ScratchRef, mut on_match_event: F) -> Result<()>
where
I: IntoIterator<Item = T>,
T: AsRef<[u8]>,
F: MatchEventHandler,
{
let (ptrs, lens): (Vec<_>, Vec<_>) = data
.into_iter()
.map(|buf| {
let buf = buf.as_ref();
(buf.as_ptr() as *const i8, buf.len() as c_uint)
})
.unzip();
unsafe {
let (callback, userdata) = on_match_event.split();
ffi::hs_scan_vector(
self.as_ptr(),
ptrs.as_slice().as_ptr() as *const *const c_char,
lens.as_slice().as_ptr() as *const _,
ptrs.len() as u32,
0,
scratch.as_ptr(),
callback,
userdata,
)
.ok()
}
}
}
const SCAN_BUF_SIZE: usize = 4096;
impl DatabaseRef<Streaming> {
pub fn scan<R, F>(&self, reader: &mut R, scratch: &ScratchRef, mut on_match_event: F) -> Result<()>
where
R: Read,
F: MatchEventHandler,
{
let stream = self.open_stream()?;
let mut buf = [0; SCAN_BUF_SIZE];
let (callback, userdata) = unsafe { on_match_event.split() };
while let Ok(len) = reader.read(&mut buf[..]) {
if len == 0 {
break;
}
stream.scan(&buf[..len], scratch, (callback, userdata))?;
}
stream.close(scratch, (callback, userdata))
}
#[cfg(feature = "async")]
pub async fn async_scan<R, F>(&self, reader: &mut R, scratch: &ScratchRef, mut on_match_event: F) -> Result<()>
where
R: AsyncRead + Unpin,
F: MatchEventHandler,
{
let stream = self.open_stream()?;
let mut buf = [0; SCAN_BUF_SIZE];
let (callback, userdata) = unsafe { on_match_event.split() };
while let Ok(len) = reader.read(&mut buf[..]).await {
if len == 0 {
break;
}
stream.scan(&buf[..len], scratch, (callback, userdata))?;
}
stream.close(scratch, (callback, userdata))
}
}
impl StreamRef {
pub fn scan<T, F>(&self, data: T, scratch: &ScratchRef, mut on_match_event: F) -> Result<()>
where
T: AsRef<[u8]>,
F: MatchEventHandler,
{
let data = data.as_ref();
unsafe {
let (callback, userdata) = on_match_event.split();
ffi::hs_scan_stream(
self.as_ptr(),
data.as_ptr() as *const c_char,
data.len() as u32,
0,
scratch.as_ptr(),
callback,
userdata,
)
.ok()
}
}
}