hyperscan 0.3.2

Hyperscan bindings for Rust with Multiple Pattern and Streaming Scan
Documentation
use std::ffi::CStr;
use std::fmt;
use std::mem::MaybeUninit;
use std::result::Result as StdResult;

use foreign_types::{ForeignType, ForeignTypeRef};
use libc::c_char;
use malloc_buf::Malloc;

use crate::common::{Database, DatabaseRef};
use crate::error::{AsResult, Error, Result};
use crate::ffi;

/// A serialized database
pub trait Serialized {
    /// The type of error if it fails in a normal fashion.
    type Error: fmt::Debug;

    /// Reporting the size that would be required by a database if it were deserialized.
    fn size(&self) -> StdResult<usize, Self::Error>;

    /// Providing information about a serialized database.
    fn info(&self) -> StdResult<String, Self::Error>;

    /// Reconstruct a pattern database from a stream of bytes previously generated by `Database::serialize()`.
    fn deserialize<M>(&self) -> StdResult<Database<M>, Self::Error>;
}

impl<T: AsRef<[u8]>> Serialized for T {
    type Error = Error;

    fn size(&self) -> Result<usize> {
        let buf = self.as_ref();
        let mut size = MaybeUninit::uninit();

        unsafe {
            ffi::hs_serialized_database_size(buf.as_ptr() as *const _, buf.len(), size.as_mut_ptr())
                .map(|_| size.assume_init())
        }
    }

    fn info(&self) -> Result<String> {
        let buf = self.as_ref();
        let mut p = MaybeUninit::uninit();

        unsafe {
            ffi::hs_serialized_database_info(buf.as_ptr() as *const _, buf.len(), p.as_mut_ptr()).and_then(|_| {
                let p = p.assume_init();
                let info = CStr::from_ptr(p).to_str()?.to_owned();
                libc::free(p as *mut _);
                Ok(info)
            })
        }
    }

    fn deserialize<M>(&self) -> Result<Database<M>> {
        let buf = self.as_ref();
        let mut db = MaybeUninit::uninit();

        unsafe {
            ffi::hs_deserialize_database(buf.as_ptr() as *const c_char, buf.len(), db.as_mut_ptr())
                .map(|_| Database::from_ptr(db.assume_init()))
        }
    }
}

impl<T> DatabaseRef<T> {
    /// Serialize a pattern database to a stream of bytes.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use hyperscan::{prelude::*, Serialized};
    /// let pattern: Pattern = r"/foo(bar)+/i".parse().unwrap();
    /// let db: BlockDatabase = pattern.left_most().build().unwrap();
    /// let buf = db.serialize().unwrap();
    /// assert!(buf.len() > 0);
    /// assert!(buf.size().unwrap() > 0);
    /// println!("database ({}) need {} bytes", buf.info().unwrap(), buf.size().unwrap());
    ///
    /// let deserialized_db: BlockDatabase = buf.deserialize().unwrap();
    /// let mut s = deserialized_db.alloc_scratch().unwrap();
    /// let mut matches = Vec::new();
    ///
    /// deserialized_db.scan("hello foobar!", &mut s, |id, from, to, _flags| {
    ///     matches.push(from..to);
    ///     Matching::Continue
    /// }).unwrap();
    ///
    /// assert_eq!(matches, vec![6..12]);
    /// ```
    pub fn serialize(&self) -> Result<Malloc<[u8]>> {
        let mut ptr = MaybeUninit::uninit();
        let mut size = MaybeUninit::uninit();

        unsafe {
            ffi::hs_serialize_database(self.as_ptr(), ptr.as_mut_ptr(), size.as_mut_ptr())
                .map(|_| Malloc::from_array(ptr.assume_init() as *mut u8, size.assume_init()))
        }
    }

    /// Reconstruct a pattern database from a stream of bytes
    /// previously generated by `DatabaseRef::serialize()` at a given memory location.
    pub fn deserialize_at<B: AsRef<[u8]>>(&mut self, bytes: B) -> Result<()> {
        let bytes = bytes.as_ref();

        unsafe { ffi::hs_deserialize_database_at(bytes.as_ptr() as *const c_char, bytes.len(), self.as_ptr()).ok() }
    }
}

#[cfg(test)]
pub mod tests {
    use crate::common::database::tests::*;
    use crate::prelude::*;

    use super::*;

    pub fn validate_serialized_database<S: Serialized>(data: &S) {
        assert!(data.size().unwrap() >= DATABASE_SIZE);

        validate_database_info(data.info().unwrap().as_str());
    }

    #[test]
    fn test_database_serialize() {
        let db: StreamingDatabase = "test".parse().unwrap();

        let data = db.serialize().unwrap();

        validate_serialized_database(&data);

        assert!(!data.info().unwrap().is_empty());
    }

    #[test]
    fn test_database_deserialize() {
        let db: VectoredDatabase = "test".parse().unwrap();

        let data = db.serialize().unwrap();

        validate_serialized_database(&data);

        let db: VectoredDatabase = data.deserialize().unwrap();

        validate_database(&db);
    }

    #[test]
    fn test_database_deserialize_at() {
        let mut db: BlockDatabase = "test".parse().unwrap();

        let data = db.serialize().unwrap();

        validate_serialized_database(&data);

        db.deserialize_at(&data).unwrap();

        validate_database(&db);
    }
}