#[cfg(feature = "stream")]
use crate::stream::LiveStream;
#[cfg(feature = "compiler")]
use crate::{
error::VectorscanCompileError,
expression::{Expression, ExpressionSet, Literal, LiteralSet},
flags::{platform::Platform, Flags, Mode},
};
use crate::{error::VectorscanRuntimeError, hs, state::Scratch};
use std::{
cmp,
ffi::CStr,
fmt, hash,
mem::{self, MaybeUninit},
ops,
os::raw::c_char,
ptr, slice, str,
};
pub type NativeDb = hs::hs_database;
#[derive(Debug)]
#[repr(transparent)]
pub struct Database(*mut NativeDb);
impl Database {
pub fn allocate_scratch(&self) -> Result<Scratch, VectorscanRuntimeError> {
let mut scratch = Scratch::blank();
scratch.setup_for_db(self)?;
Ok(scratch)
}
#[cfg(feature = "stream")]
#[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
pub fn allocate_stream(&self) -> Result<LiveStream, VectorscanRuntimeError> {
LiveStream::open(self)
}
pub fn serialize(&self) -> Result<SerializedDb<'static>, VectorscanRuntimeError> {
SerializedDb::serialize_db(self)
}
}
#[cfg(feature = "compiler")]
#[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
impl Database {
pub fn compile(
expression: &Expression,
flags: Flags,
mode: Mode,
platform: Option<&Platform>,
) -> Result<Self, VectorscanCompileError> {
let mut db = ptr::null_mut();
let mut compile_err = ptr::null_mut();
let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
VectorscanRuntimeError::copy_from_native_compile_error(
unsafe {
hs::hs_compile(
expression.as_ptr(),
flags.into_native(),
mode.into_native(),
platform
.as_ref()
.map(|p| p as *const hs::hs_platform_info)
.unwrap_or(ptr::null()),
&mut db,
&mut compile_err,
)
},
compile_err,
)?;
Ok(unsafe { Self::from_native(db) })
}
pub fn compile_multi(
expression_set: &ExpressionSet,
mode: Mode,
platform: Option<&Platform>,
) -> Result<Self, VectorscanCompileError> {
let mut db = ptr::null_mut();
let mut compile_err = ptr::null_mut();
let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
VectorscanRuntimeError::copy_from_native_compile_error(
unsafe {
if let Some(exts_ptr) = expression_set.exts_ptr() {
hs::hs_compile_ext_multi(
expression_set.expressions_ptr(),
expression_set.flags_ptr(),
expression_set.ids_ptr(),
exts_ptr,
expression_set.num_elements(),
mode.into_native(),
platform
.as_ref()
.map(|p| p as *const hs::hs_platform_info)
.unwrap_or(ptr::null()),
&mut db,
&mut compile_err,
)
} else {
hs::hs_compile_multi(
expression_set.expressions_ptr(),
expression_set.flags_ptr(),
expression_set.ids_ptr(),
expression_set.num_elements(),
mode.into_native(),
platform
.as_ref()
.map(|p| p as *const hs::hs_platform_info)
.unwrap_or(ptr::null()),
&mut db,
&mut compile_err,
)
}
},
compile_err,
)?;
Ok(unsafe { Self::from_native(db) })
}
pub fn compile_literal(
literal: &Literal,
flags: Flags,
mode: Mode,
platform: Option<&Platform>,
) -> Result<Self, VectorscanCompileError> {
let mut db = ptr::null_mut();
let mut compile_err = ptr::null_mut();
let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
VectorscanRuntimeError::copy_from_native_compile_error(
unsafe {
hs::hs_compile_lit(
literal.as_ptr(),
flags.into_native(),
literal.as_bytes().len(),
mode.into_native(),
platform
.as_ref()
.map(|p| p as *const hs::hs_platform_info)
.unwrap_or(ptr::null()),
&mut db,
&mut compile_err,
)
},
compile_err,
)?;
Ok(unsafe { Self::from_native(db) })
}
pub fn compile_multi_literal(
literal_set: &LiteralSet,
mode: Mode,
platform: Option<&Platform>,
) -> Result<Self, VectorscanCompileError> {
let mut db = ptr::null_mut();
let mut compile_err = ptr::null_mut();
let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
VectorscanRuntimeError::copy_from_native_compile_error(
unsafe {
hs::hs_compile_lit_multi(
literal_set.literals_ptr(),
literal_set.flags_ptr(),
literal_set.ids_ptr(),
literal_set.lengths_ptr(),
literal_set.num_elements(),
mode.into_native(),
platform
.as_ref()
.map(|p| p as *const hs::hs_platform_info)
.unwrap_or(ptr::null()),
&mut db,
&mut compile_err,
)
},
compile_err,
)?;
Ok(unsafe { Self::from_native(db) })
}
}
impl Database {
pub fn database_size(&self) -> Result<usize, VectorscanRuntimeError> {
let mut ret: MaybeUninit<usize> = MaybeUninit::uninit();
VectorscanRuntimeError::from_native(unsafe {
hs::hs_database_size(self.as_ref_native(), ret.as_mut_ptr())
})?;
Ok(unsafe { ret.assume_init() })
}
#[cfg(feature = "stream")]
#[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
pub fn stream_size(&self) -> Result<usize, VectorscanRuntimeError> {
let mut ret: MaybeUninit<usize> = MaybeUninit::uninit();
VectorscanRuntimeError::from_native(unsafe {
hs::hs_stream_size(self.as_ref_native(), ret.as_mut_ptr())
})?;
Ok(unsafe { ret.assume_init() })
}
pub fn info(&self) -> Result<DbInfo, VectorscanRuntimeError> { DbInfo::extract_db_info(self) }
}
impl Database {
pub const unsafe fn from_native(p: *mut NativeDb) -> Self { Self(p) }
pub fn as_ref_native(&self) -> &NativeDb { unsafe { &*self.0 } }
pub fn as_mut_native(&mut self) -> &mut NativeDb { unsafe { &mut *self.0 } }
pub unsafe fn try_drop(&mut self) -> Result<(), VectorscanRuntimeError> {
VectorscanRuntimeError::from_native(unsafe { hs::hs_free_database(self.as_mut_native()) })
}
}
impl ops::Drop for Database {
fn drop(&mut self) {
unsafe {
self.try_drop().unwrap();
}
}
}
unsafe impl Send for Database {}
pub mod alloc {
use std::{borrow::Cow, ops, slice};
#[derive(Debug)]
pub struct MiscAllocation {
pub(crate) data: *mut u8,
pub(crate) len: usize,
}
unsafe impl Send for MiscAllocation {}
impl MiscAllocation {
pub(crate) const fn as_ptr(&self) -> *mut u8 { self.data }
pub(crate) const fn len(&self) -> usize { self.len }
pub const fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
}
unsafe fn free(&mut self) { crate::free_misc(self.data) }
}
impl ops::Drop for MiscAllocation {
fn drop(&mut self) {
unsafe {
self.free();
}
}
}
#[derive(Debug)]
pub enum DbAllocation<'a> {
Misc(MiscAllocation),
Rust(Cow<'a, [u8]>),
}
impl<'a> DbAllocation<'a> {
pub(crate) fn as_ptr(&self) -> *const u8 {
match self {
Self::Misc(misc) => misc.as_ptr(),
Self::Rust(cow) => cow.as_ptr(),
}
}
pub(crate) fn len(&self) -> usize {
match self {
Self::Misc(misc) => misc.len(),
Self::Rust(cow) => cow.len(),
}
}
pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) } }
}
impl DbAllocation<'static> {
pub fn from_cloned_data(s: &DbAllocation) -> Self {
let newly_allocated: Vec<u8> = s.as_slice().to_vec();
Self::Rust(Cow::Owned(newly_allocated))
}
}
impl Clone for DbAllocation<'static> {
fn clone(&self) -> Self { Self::from_cloned_data(self) }
}
#[cfg(feature = "chimera")]
#[cfg_attr(docsrs, doc(cfg(feature = "chimera")))]
pub mod chimera {
use std::{ops, slice};
#[derive(Debug)]
pub struct ChimeraMiscAllocation {
pub(crate) data: *mut u8,
pub(crate) len: usize,
}
unsafe impl Send for ChimeraMiscAllocation {}
impl ChimeraMiscAllocation {
pub(crate) const fn as_ptr(&self) -> *mut u8 { self.data }
pub(crate) const fn len(&self) -> usize { self.len }
pub const fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
}
unsafe fn free(&mut self) { crate::free_misc_chimera(self.data) }
}
impl ops::Drop for ChimeraMiscAllocation {
fn drop(&mut self) {
unsafe {
self.free();
}
}
}
}
}
#[repr(transparent)]
pub struct DbInfo(pub alloc::MiscAllocation);
impl DbInfo {
const fn without_null(&self) -> impl slice::SliceIndex<[u8], Output=[u8]> { ..(self.0.len() - 1) }
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.0.as_slice()[self.without_null()]) }
}
pub fn extract_db_info(db: &Database) -> Result<Self, VectorscanRuntimeError> {
let mut info = ptr::null_mut();
VectorscanRuntimeError::from_native(unsafe {
hs::hs_database_info(db.as_ref_native(), &mut info)
})?;
let len = unsafe { CStr::from_ptr(info) }.to_bytes_with_nul().len();
assert!(len > 0);
let ret = alloc::MiscAllocation {
data: unsafe { mem::transmute(info) },
len,
};
Ok(Self(ret))
}
}
impl fmt::Debug for DbInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DbInfo({:?})", self.as_str()) }
}
impl fmt::Display for DbInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_str()) }
}
impl cmp::PartialEq for DbInfo {
fn eq(&self, other: &Self) -> bool { self.as_str().eq(other.as_str()) }
}
impl cmp::Eq for DbInfo {}
impl cmp::PartialOrd for DbInfo {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
}
impl cmp::Ord for DbInfo {
fn cmp(&self, other: &Self) -> cmp::Ordering { self.as_str().cmp(other.as_str()) }
}
impl hash::Hash for DbInfo {
fn hash<H>(&self, state: &mut H)
where H: hash::Hasher {
self.as_str().hash(state);
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct SerializedDb<'a>(
pub alloc::DbAllocation<'a>,
);
impl<'a> SerializedDb<'a> {
fn as_ptr(&self) -> *const c_char { unsafe { mem::transmute(self.0.as_ptr()) } }
fn len(&self) -> usize { self.0.len() }
pub fn deserialize_db(&self) -> Result<Database, VectorscanRuntimeError> {
let mut deserialized: MaybeUninit<*mut hs::hs_database> = MaybeUninit::uninit();
VectorscanRuntimeError::from_native(unsafe {
hs::hs_deserialize_database(self.as_ptr(), self.len(), deserialized.as_mut_ptr())
})?;
let deserialized = unsafe { deserialized.assume_init() };
Ok(unsafe { Database::from_native(deserialized) })
}
pub fn deserialized_size(&self) -> Result<usize, VectorscanRuntimeError> {
let mut deserialized_size: MaybeUninit<usize> = MaybeUninit::uninit();
VectorscanRuntimeError::from_native(unsafe {
hs::hs_serialized_database_size(self.as_ptr(), self.len(), deserialized_size.as_mut_ptr())
})?;
let deserialized_size = unsafe { deserialized_size.assume_init() };
Ok(deserialized_size)
}
pub unsafe fn deserialize_db_at(&self, db: *mut NativeDb) -> Result<(), VectorscanRuntimeError> {
VectorscanRuntimeError::from_native(hs::hs_deserialize_database_at(
self.as_ptr(),
self.len(),
db,
))
}
pub fn extract_db_info(&self) -> Result<DbInfo, VectorscanRuntimeError> {
let mut info = ptr::null_mut();
VectorscanRuntimeError::from_native(unsafe {
hs::hs_serialized_database_info(self.as_ptr(), self.len(), &mut info)
})?;
let len = unsafe { CStr::from_ptr(info) }.to_bytes_with_nul().len();
assert!(len > 0);
let ret = alloc::MiscAllocation {
data: info as *mut u8,
len,
};
Ok(DbInfo(ret))
}
}
impl SerializedDb<'static> {
pub fn serialize_db(db: &Database) -> Result<Self, VectorscanRuntimeError> {
let mut data = ptr::null_mut();
let mut len: usize = 0;
VectorscanRuntimeError::from_native(unsafe {
hs::hs_serialize_database(db.as_ref_native(), &mut data, &mut len)
})?;
let data = data as *mut u8;
Ok(Self(alloc::DbAllocation::Misc(alloc::MiscAllocation {
data,
len,
})))
}
pub fn from_cloned_data(s: &SerializedDb) -> Self {
let SerializedDb(ref s) = s;
Self(alloc::DbAllocation::from_cloned_data(s))
}
}
impl Clone for SerializedDb<'static> {
fn clone(&self) -> Self { Self::from_cloned_data(self) }
}
#[cfg(feature = "chimera")]
#[cfg_attr(docsrs, doc(cfg(feature = "chimera")))]
pub mod chimera {
use super::alloc::chimera::ChimeraMiscAllocation;
#[cfg(feature = "compiler")]
use super::Platform;
#[cfg(feature = "compiler")]
use crate::{
error::chimera::ChimeraCompileError,
expression::chimera::{ChimeraExpression, ChimeraExpressionSet, ChimeraMatchLimits},
flags::chimera::{ChimeraFlags, ChimeraMode},
};
use crate::{error::chimera::ChimeraRuntimeError, hs, state::chimera::ChimeraScratch};
use std::{cmp, ffi::CStr, fmt, hash, mem, ops, ptr, slice, str};
pub type NativeChimeraDb = hs::ch_database;
#[derive(Debug)]
#[repr(transparent)]
pub struct ChimeraDb(*mut NativeChimeraDb);
impl ChimeraDb {
pub fn allocate_scratch(&self) -> Result<ChimeraScratch, ChimeraRuntimeError> {
let mut scratch = ChimeraScratch::blank();
scratch.setup_for_db(self)?;
Ok(scratch)
}
}
#[cfg(feature = "compiler")]
#[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
impl ChimeraDb {
pub fn compile(
expression: &ChimeraExpression,
flags: ChimeraFlags,
mode: ChimeraMode,
platform: Option<&Platform>,
) -> Result<Self, ChimeraCompileError> {
let mut db = ptr::null_mut();
let mut compile_err = ptr::null_mut();
let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
ChimeraRuntimeError::copy_from_native_compile_error(
unsafe {
hs::ch_compile(
expression.as_ptr(),
flags.into_native(),
mode.into_native(),
platform
.as_ref()
.map(|p| p as *const hs::hs_platform_info)
.unwrap_or(ptr::null()),
&mut db,
&mut compile_err,
)
},
compile_err,
)?;
Ok(unsafe { Self::from_native(db) })
}
pub fn compile_multi(
exprs: &ChimeraExpressionSet,
mode: ChimeraMode,
platform: Option<&Platform>,
) -> Result<Self, ChimeraCompileError> {
let mut db = ptr::null_mut();
let mut compile_err = ptr::null_mut();
let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
ChimeraRuntimeError::copy_from_native_compile_error(
unsafe {
if let Some(ChimeraMatchLimits {
match_limit,
match_limit_recursion,
}) = exprs.limits()
{
hs::ch_compile_ext_multi(
exprs.expressions_ptr(),
exprs.flags_ptr(),
exprs.ids_ptr(),
exprs.num_elements(),
mode.into_native(),
match_limit,
match_limit_recursion,
platform
.as_ref()
.map(|p| p as *const hs::hs_platform_info)
.unwrap_or(ptr::null()),
&mut db,
&mut compile_err,
)
} else {
hs::ch_compile_multi(
exprs.expressions_ptr(),
exprs.flags_ptr(),
exprs.ids_ptr(),
exprs.num_elements(),
mode.into_native(),
platform
.as_ref()
.map(|p| p as *const hs::hs_platform_info)
.unwrap_or(ptr::null()),
&mut db,
&mut compile_err,
)
}
},
compile_err,
)?;
Ok(unsafe { Self::from_native(db) })
}
}
impl ChimeraDb {
pub fn database_size(&self) -> Result<usize, ChimeraRuntimeError> {
let mut database_size: usize = 0;
ChimeraRuntimeError::from_native(unsafe {
hs::ch_database_size(self.as_ref_native(), &mut database_size)
})?;
Ok(database_size)
}
pub fn info(&self) -> Result<ChimeraDbInfo, ChimeraRuntimeError> {
ChimeraDbInfo::extract_db_info(self)
}
}
impl ChimeraDb {
pub const unsafe fn from_native(p: *mut NativeChimeraDb) -> Self { Self(p) }
pub fn as_ref_native(&self) -> &NativeChimeraDb { unsafe { &*self.0 } }
pub fn as_mut_native(&mut self) -> &mut NativeChimeraDb { unsafe { &mut *self.0 } }
pub unsafe fn try_drop(&mut self) -> Result<(), ChimeraRuntimeError> {
ChimeraRuntimeError::from_native(hs::ch_free_database(self.as_mut_native()))
}
}
impl ops::Drop for ChimeraDb {
fn drop(&mut self) {
unsafe {
self.try_drop().unwrap();
}
}
}
pub struct ChimeraDbInfo(ChimeraMiscAllocation);
impl ChimeraDbInfo {
const fn without_null(&self) -> impl slice::SliceIndex<[u8], Output=[u8]> {
..(self.0.len() - 1)
}
pub fn as_str(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.0.as_slice()[self.without_null()]) }
}
pub fn extract_db_info(db: &ChimeraDb) -> Result<Self, ChimeraRuntimeError> {
let mut info = ptr::null_mut();
ChimeraRuntimeError::from_native(unsafe {
hs::ch_database_info(db.as_ref_native(), &mut info)
})?;
let len = unsafe { CStr::from_ptr(info) }.to_bytes_with_nul().len();
assert!(len > 0);
let ret = ChimeraMiscAllocation {
data: unsafe { mem::transmute(info) },
len,
};
Ok(Self(ret))
}
}
impl fmt::Debug for ChimeraDbInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ChimeraDbInfo({:?})", self.as_str())
}
}
impl fmt::Display for ChimeraDbInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_str()) }
}
impl cmp::PartialEq for ChimeraDbInfo {
fn eq(&self, other: &Self) -> bool { self.as_str().eq(other.as_str()) }
}
impl cmp::Eq for ChimeraDbInfo {}
impl cmp::PartialOrd for ChimeraDbInfo {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
}
impl cmp::Ord for ChimeraDbInfo {
fn cmp(&self, other: &Self) -> cmp::Ordering { self.as_str().cmp(other.as_str()) }
}
impl hash::Hash for ChimeraDbInfo {
fn hash<H>(&self, state: &mut H)
where H: hash::Hasher {
self.as_str().hash(state);
}
}
}