vectorscan/
database.rs

1/* Copyright 2022-2024 Danny McClanahan */
2/* SPDX-License-Identifier: BSD-3-Clause */
3
4//! Compile state machines from expressions or deserialize them from bytes.
5//!
6//! Vectorscan supports two distinct types of databases:
7//! - [`Database`]: from the base vectorscan library and supports [`Expression`]
8//!   and [`Literal`] patterns.
9//! - [`chimera::ChimeraDb`]: from the chimera library and supports
10//!   [`ChimeraExpression`](crate::expression::chimera::ChimeraExpression)
11//!   patterns.
12//!
13//! Each database type serves as the entry point to the pattern compiler
14//! methods, such as [`Database::compile()`] and
15//! [`chimera::ChimeraDb::compile()`].
16//!
17//! # Database Instantiation
18//! The base vectorscan library offers a serialization interface for database
19//! objects which allows them to be transferred across hosts. The
20//! [`SerializedDb`] type provides an interface to locate serialized data from
21//! multiple locations. Consumers of this crate which disable the `"compiler"`
22//! feature can still search against strings by deserializing a database from
23//! bytes.
24//!
25//! The chimera library does not support database serialization, so databases
26//! must be created by compilation.
27
28#[cfg(feature = "stream")]
29use crate::stream::LiveStream;
30#[cfg(feature = "compiler")]
31use crate::{
32  error::VectorscanCompileError,
33  expression::{Expression, ExpressionSet, Literal, LiteralSet},
34  flags::{platform::Platform, Flags, Mode},
35};
36use crate::{error::VectorscanRuntimeError, hs, state::Scratch};
37
38use std::{
39  cmp,
40  ffi::CStr,
41  fmt, hash,
42  mem::{self, MaybeUninit},
43  ops,
44  os::raw::c_char,
45  ptr, slice, str,
46};
47
48/// Pointer type for db allocations used in [`Database#Managing
49/// Allocations`](Database#managing-allocations).
50pub type NativeDb = hs::hs_database;
51
52/// Read-only description of an in-memory state machine.
53///
54/// This type also serves as the entry point to the various types of [pattern
55/// compilers](#pattern-compilers), including literals, sets, and literal sets.
56#[derive(Debug)]
57#[repr(transparent)]
58pub struct Database(*mut NativeDb);
59
60/// # Convenience Methods
61/// These methods prepare some resource within a new heap allocation and are
62/// useful for doctests and examples.
63///
64/// ## Scratch Setup
65/// Databases already require their own heap allocation, which can be managed
66/// with the methods in [Managing Allocations](#managing-allocations). However,
67/// databases also impose a sort of implicit dynamic lifetime constraint on
68/// [`Scratch`] objects, which must be initialized against a db with
69/// [`Scratch::setup_for_db()`] before vectorscan can do any searching.
70///
71/// It is encouraged to re-use [`Scratch`] objects across databases where
72/// possible to minimize unnecessary allocations, but
73/// [`Self::allocate_scratch()`] is provided as a convenience method to quickly
74/// produce a 1:1 db:scratch mapping.
75///
76/// ## Serialization
77/// While [`SerializedDb`] offers a rich interface to wrap serialized bytes from
78/// a variety of sources with [`alloc::DbAllocation`], [`Self::serialize()`]
79/// simply returns a newly allocated region of bytes.
80impl Database {
81  /// Call [`Scratch::setup_for_db()`] on a newly allocated [`Scratch::blank`].
82  ///
83  ///```
84  /// #[cfg(feature = "compiler")]
85  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
86  ///   use vectorscan::{expression::*, flags::*, matchers::*};
87  ///
88  ///   let expr: Expression = "a+".parse()?;
89  ///   let db = expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?;
90  ///   let mut scratch = db.allocate_scratch()?;
91  ///
92  ///   let mut matches: Vec<&str> = Vec::new();
93  ///   scratch
94  ///     .scan_sync(&db, "aardvark".into(), |Match { source, .. }| {
95  ///       matches.push(unsafe { source.as_str() });
96  ///       MatchResult::Continue
97  ///     })?;
98  ///   assert_eq!(&matches, &["a", "aa", "a"]);
99  ///   Ok(())
100  /// }
101  /// # #[cfg(not(feature = "compiler"))]
102  /// # fn main() {}
103  /// ```
104  pub fn allocate_scratch(&self) -> Result<Scratch, VectorscanRuntimeError> {
105    let mut scratch = Scratch::blank();
106    scratch.setup_for_db(self)?;
107    Ok(scratch)
108  }
109
110  /// Call [`LiveStream::open()`] on `self`.
111  ///
112  ///```
113  /// #[cfg(feature = "compiler")]
114  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
115  ///   use vectorscan::{expression::*, flags::*, matchers::*, stream::*};
116  ///   use std::ops::Range;
117  ///
118  ///   let expr: Expression = "a+".parse()?;
119  ///   let db = expr.compile(Flags::SOM_LEFTMOST, Mode::STREAM | Mode::SOM_HORIZON_SMALL)?;
120  ///   let scratch = db.allocate_scratch()?;
121  ///   let live = db.allocate_stream()?;
122  ///
123  ///   let data = "aardvark";
124  ///   let mut matches: Vec<&str> = Vec::new();
125  ///   let mut match_fn = |StreamMatch { range, .. }| {
126  ///     let range: Range<usize> = range.into();
127  ///     matches.push(&data[range]);
128  ///     MatchResult::Continue
129  ///   };
130  ///   {
131  ///     let matcher = StreamMatcher::new(&mut match_fn);
132  ///     let mut sink = ScratchStreamSink::new(live, matcher, scratch);
133  ///
134  ///     sink.scan(data.into())?;
135  ///     sink.flush_eod()?;
136  ///   }
137  ///   assert_eq!(&matches, &["a", "aa", "a"]);
138  ///   Ok(())
139  /// }
140  /// # #[cfg(not(feature = "compiler"))]
141  /// # fn main() {}
142  /// ```
143  #[cfg(feature = "stream")]
144  #[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
145  pub fn allocate_stream(&self) -> Result<LiveStream, VectorscanRuntimeError> {
146    LiveStream::open(self)
147  }
148
149  /// Allocate a new memory region and serialize this in-memory state machine
150  /// into it.
151  ///
152  ///```
153  /// #[cfg(feature = "compiler")]
154  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
155  ///   use vectorscan::{expression::*, flags::*, matchers::*};
156  ///
157  ///   // Create a db to match against:
158  ///   let expr: Expression = "a+".parse()?;
159  ///   let db = expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?;
160  ///
161  ///   // Serialize and deserialize the db:
162  ///   let db = db.serialize()?.deserialize_db()?;
163  ///   let mut scratch = db.allocate_scratch()?;
164  ///
165  ///   // Search against the db:
166  ///   let mut matches: Vec<&str> = Vec::new();
167  ///   scratch
168  ///     .scan_sync(&db, "aardvark".into(), |Match { source, .. }| {
169  ///       matches.push(unsafe { source.as_str() });
170  ///       MatchResult::Continue
171  ///     })?;
172  ///   assert_eq!(&matches, &["a", "aa", "a"]);
173  ///   Ok(())
174  /// }
175  /// # #[cfg(not(feature = "compiler"))]
176  /// # fn main() {}
177  /// ```
178  pub fn serialize(&self) -> Result<SerializedDb<'static>, VectorscanRuntimeError> {
179    SerializedDb::serialize_db(self)
180  }
181}
182
183/// # Pattern Compilers
184/// Vectorscan supports compiling state machines for PCRE-like and literal
185/// pattern strings, as well as parallel sets of those patterns (although note
186/// that literal and non-literal patterns cannot be mixed). Each compile method
187/// supports a subset of all [`Flags`] arguments, documented in each method.
188///
189/// ## Platform Compatibility
190/// Each method also accepts an optional [`Platform`] object,
191/// which is used to select processor features to compile the database for.
192/// While the default of [`None`] will enable all features available to the
193/// current processor, some features can be disabled in order to produce a
194/// database which can execute on a wider variety of target platforms
195/// after being deserialized from a remote source.
196///
197///```
198/// #[cfg(all(feature = "compiler", feature = "stream"))]
199/// fn main() -> Result<(), vectorscan::error::VectorscanError> {
200///   use vectorscan::{expression::*, flags::{*, platform::*}, database::*};
201///   use std::slice;
202///
203///   let expr: Expression = "a+".parse()?;
204///
205///   // Verify that the current platform has AVX2 instructions, and make a db:
206///   let plat = Platform::local()?;
207///   assert!(plat.cpu_features.contains(&CpuFeatures::AVX2));
208///   assert!(plat != Platform::GENERIC);
209///   let db_with_avx2 = Database::compile(
210///     &expr,
211///     Flags::default(),
212///     Mode::STREAM,
213///     Some(&plat),
214///   )?;
215///
216///   // The only specialized instructions we have available are AVX2:
217///   assert_eq!(CpuFeatures::NONE, plat.cpu_features & !CpuFeatures::AVX2);
218///   // Avoid using AVX2 instructions:
219///   let db_no_avx2 = Database::compile(
220///     &expr,
221///     Flags::default(),
222///     Mode::STREAM,
223///     Some(&Platform::GENERIC),
224///   )?;
225///
226///   // Instruction selection does not affect the size of the state machine:
227///   assert!(db_with_avx2.database_size()? == db_no_avx2.database_size()?);
228///   assert!(db_with_avx2.stream_size()? == db_no_avx2.stream_size()?);
229///
230///   // Now create a db with None for the platform:
231///   let db_local = Database::compile(&expr, Flags::default(), Mode::STREAM, None)?;
232///   assert!(db_with_avx2.database_size()? == db_local.database_size()?);
233///   let n = db_with_avx2.database_size()?;
234///
235///   // Using None produces the same db as Platform::local():
236///   assert!(db_with_avx2.info()? == db_local.info()?);
237///   assert!(db_no_avx2.info()? != db_local.info()?);
238///
239///   // The "same" db does *not* apply to the in-memory representation:
240///   let db_data_1 = unsafe { slice::from_raw_parts(
241///     db_with_avx2.as_ref_native() as *const NativeDb as *const u8,
242///     n,
243///   )};
244///   let db_data_2 = unsafe { slice::from_raw_parts(
245///     db_no_avx2.as_ref_native() as *const NativeDb as *const u8,
246///     n,
247///   )};
248///   let db_data_3 = unsafe { slice::from_raw_parts(
249///     db_local.as_ref_native() as *const NativeDb as *const u8,
250///     n,
251///   )};
252///   assert!(db_data_1 != db_data_3);
253///   assert!(db_data_1 != db_data_2);
254///   Ok(())
255/// }
256/// # #[cfg(not(all(feature = "compiler", feature = "stream")))]
257/// # fn main() {}
258/// ```
259///
260/// ## Dynamic Memory Allocation
261/// These methods allocate a new region of memory using the db allocator (which
262/// can be overridden with [`crate::alloc::set_db_allocator()`]). That
263/// allocation can be manipulated as described in [Managing
264/// Allocations](#managing-allocations).
265#[cfg(feature = "compiler")]
266#[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
267impl Database {
268  /// Single pattern compiler.
269  ///
270  /// # Accepted Flags
271  /// - [`CASELESS`](Flags::CASELESS)
272  /// - [`DOTALL`](Flags::DOTALL)
273  /// - [`MULTILINE`](Flags::MULTILINE)
274  /// - [`SINGLEMATCH`](Flags::SINGLEMATCH)
275  /// - [`ALLOWEMPTY`](Flags::ALLOWEMPTY)
276  /// - [`UTF8`](Flags::UTF8)
277  /// - [`UCP`](Flags::UCP)
278  /// - [`PREFILTER`](Flags::PREFILTER)
279  /// - [`SOM_LEFTMOST`](Flags::SOM_LEFTMOST)
280  /// - [`COMBINATION`](Flags::COMBINATION)
281  /// - [`QUIET`](Flags::QUIET)
282  ///
283  ///```
284  /// # fn main() -> Result<(), vectorscan::error::VectorscanError> {
285  /// use vectorscan::{expression::*, flags::*, database::*, matchers::*};
286  ///
287  /// let expr: Expression = "hell(o)?".parse()?;
288  /// let db = Database::compile(&expr, Flags::default(), Mode::BLOCK, None)?;
289  ///
290  /// let mut scratch = db.allocate_scratch()?;
291  ///
292  /// let mut matches: Vec<&str> = Vec::new();
293  /// scratch
294  ///   .scan_sync(&db, "hello".into(), |m| {
295  ///     matches.push(unsafe { m.source.as_str() });
296  ///     MatchResult::Continue
297  ///   })?;
298  /// assert_eq!(&matches, &["hell", "hello"]);
299  /// # Ok(())
300  /// # }
301  /// ```
302  pub fn compile(
303    expression: &Expression,
304    flags: Flags,
305    mode: Mode,
306    platform: Option<&Platform>,
307  ) -> Result<Self, VectorscanCompileError> {
308    let mut db = ptr::null_mut();
309    let mut compile_err = ptr::null_mut();
310    let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
311    VectorscanRuntimeError::copy_from_native_compile_error(
312      unsafe {
313        hs::hs_compile(
314          expression.as_ptr(),
315          flags.into_native(),
316          mode.into_native(),
317          platform
318            .as_ref()
319            .map(|p| p as *const hs::hs_platform_info)
320            .unwrap_or(ptr::null()),
321          &mut db,
322          &mut compile_err,
323        )
324      },
325      compile_err,
326    )?;
327    Ok(unsafe { Self::from_native(db) })
328  }
329
330  /// Multiple pattern compiler.
331  ///
332  /// # Accepted Flags
333  /// - [`CASELESS`](Flags::CASELESS)
334  /// - [`DOTALL`](Flags::DOTALL)
335  /// - [`MULTILINE`](Flags::MULTILINE)
336  /// - [`SINGLEMATCH`](Flags::SINGLEMATCH)
337  /// - [`ALLOWEMPTY`](Flags::ALLOWEMPTY)
338  /// - [`UTF8`](Flags::UTF8)
339  /// - [`UCP`](Flags::UCP)
340  /// - [`PREFILTER`](Flags::PREFILTER)
341  /// - [`SOM_LEFTMOST`](Flags::SOM_LEFTMOST)
342  /// - [`COMBINATION`](Flags::COMBINATION)
343  /// - [`QUIET`](Flags::QUIET)
344  ///
345  ///```
346  /// # fn main() -> Result<(), vectorscan::error::VectorscanError> {
347  /// use vectorscan::{expression::*, flags::*, database::*, matchers::*};
348  ///
349  /// let a_expr: Expression = "a+".parse()?;
350  /// let b_expr: Expression = "b+".parse()?;
351  ///
352  /// // Example of providing ExprExt info (not available in ::compile()!):
353  /// let ext = ExprExt::from_min_length(1);
354  ///
355  /// let expr_set = ExpressionSet::from_exprs([&a_expr, &b_expr])
356  ///   .with_ids([ExprId(1), ExprId(2)])
357  ///   .with_exts([None, Some(&ext)]);
358  ///
359  /// let db = Database::compile_multi(&expr_set, Mode::BLOCK, None)?;
360  ///
361  /// let mut scratch = db.allocate_scratch()?;
362  ///
363  /// let mut matches: Vec<&str> = Vec::new();
364  /// scratch
365  ///   .scan_sync(&db, "aardvark".into(), |m| {
366  ///     matches.push(unsafe { m.source.as_str() });
367  ///     MatchResult::Continue
368  ///   })?;
369  /// assert_eq!(&matches, &["a", "aa", "aardva"]);
370  ///
371  /// matches.clear();
372  /// scratch
373  ///   .scan_sync(&db, "imbibe".into(), |m| {
374  ///     matches.push(unsafe { m.source.as_str() });
375  ///     MatchResult::Continue
376  ///   })?;
377  /// assert_eq!(&matches, &["imb", "imbib"]);
378  /// # Ok(())
379  /// # }
380  /// ```
381  pub fn compile_multi(
382    expression_set: &ExpressionSet,
383    mode: Mode,
384    platform: Option<&Platform>,
385  ) -> Result<Self, VectorscanCompileError> {
386    let mut db = ptr::null_mut();
387    let mut compile_err = ptr::null_mut();
388    let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
389    VectorscanRuntimeError::copy_from_native_compile_error(
390      unsafe {
391        if let Some(exts_ptr) = expression_set.exts_ptr() {
392          hs::hs_compile_ext_multi(
393            expression_set.expressions_ptr(),
394            expression_set.flags_ptr(),
395            expression_set.ids_ptr(),
396            exts_ptr,
397            expression_set.num_elements(),
398            mode.into_native(),
399            platform
400              .as_ref()
401              .map(|p| p as *const hs::hs_platform_info)
402              .unwrap_or(ptr::null()),
403            &mut db,
404            &mut compile_err,
405          )
406        } else {
407          hs::hs_compile_multi(
408            expression_set.expressions_ptr(),
409            expression_set.flags_ptr(),
410            expression_set.ids_ptr(),
411            expression_set.num_elements(),
412            mode.into_native(),
413            platform
414              .as_ref()
415              .map(|p| p as *const hs::hs_platform_info)
416              .unwrap_or(ptr::null()),
417            &mut db,
418            &mut compile_err,
419          )
420        }
421      },
422      compile_err,
423    )?;
424    Ok(unsafe { Self::from_native(db) })
425  }
426
427  /// Single literal compiler.
428  ///
429  /// # Accepted Flags
430  /// - [`CASELESS`](Flags::CASELESS)
431  /// - [`SINGLEMATCH`](Flags::SINGLEMATCH)
432  /// - [`SOM_LEFTMOST`](Flags::SOM_LEFTMOST)
433  ///
434  ///```
435  /// # fn main() -> Result<(), vectorscan::error::VectorscanError> {
436  /// use vectorscan::{expression::*, flags::*, database::*, matchers::*};
437  ///
438  /// let expr: Literal = "he\0ll".parse()?;
439  /// let db = Database::compile_literal(&expr, Flags::default(), Mode::BLOCK, None)?;
440  ///
441  /// let mut scratch = db.allocate_scratch()?;
442  ///
443  /// let mut matches: Vec<&str> = Vec::new();
444  /// scratch
445  ///   .scan_sync(&db, "he\0llo".into(), |m| {
446  ///     matches.push(unsafe { m.source.as_str() });
447  ///     MatchResult::Continue
448  ///   })?;
449  /// assert_eq!(&matches, &["he\0ll"]);
450  /// # Ok(())
451  /// # }
452  /// ```
453  pub fn compile_literal(
454    literal: &Literal,
455    flags: Flags,
456    mode: Mode,
457    platform: Option<&Platform>,
458  ) -> Result<Self, VectorscanCompileError> {
459    let mut db = ptr::null_mut();
460    let mut compile_err = ptr::null_mut();
461    let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
462    VectorscanRuntimeError::copy_from_native_compile_error(
463      unsafe {
464        hs::hs_compile_lit(
465          literal.as_ptr(),
466          flags.into_native(),
467          literal.as_bytes().len(),
468          mode.into_native(),
469          platform
470            .as_ref()
471            .map(|p| p as *const hs::hs_platform_info)
472            .unwrap_or(ptr::null()),
473          &mut db,
474          &mut compile_err,
475        )
476      },
477      compile_err,
478    )?;
479    Ok(unsafe { Self::from_native(db) })
480  }
481
482  /// Multiple literal compiler.
483  ///
484  /// # Accepted Flags
485  /// - [`CASELESS`](Flags::CASELESS)
486  /// - [`SINGLEMATCH`](Flags::SINGLEMATCH)
487  /// - [`SOM_LEFTMOST`](Flags::SOM_LEFTMOST)
488  ///
489  ///```
490  /// # fn main() -> Result<(), vectorscan::error::VectorscanError> {
491  /// use vectorscan::{expression::*, flags::*, database::*, matchers::*};
492  ///
493  /// let hell_lit: Literal = "he\0ll".parse()?;
494  /// let free_lit: Literal = "fr\0e\0e".parse()?;
495  /// let lit_set = LiteralSet::from_lits([&hell_lit, &free_lit])
496  ///   .with_flags([Flags::default(), Flags::default()])
497  ///   .with_ids([ExprId(2), ExprId(1)]);
498  ///
499  /// let db = Database::compile_multi_literal(&lit_set, Mode::BLOCK, None)?;
500  ///
501  /// let mut scratch = db.allocate_scratch()?;
502  ///
503  /// let mut matches: Vec<(u32, &str)> = Vec::new();
504  /// scratch
505  ///   .scan_sync(
506  ///     &db,
507  ///     "he\0llo".into(),
508  ///     |Match { id: ExpressionIndex(id), source, .. }| {
509  ///       matches.push((id, unsafe { source.as_str() }));
510  ///       MatchResult::Continue
511  ///     })?;
512  /// assert_eq!(&matches, &[(2, "he\0ll")]);
513  ///
514  /// matches.clear();
515  /// scratch
516  ///   .scan_sync(
517  ///     &db,
518  ///     "fr\0e\0edom".into(),
519  ///     |Match { id: ExpressionIndex(id), source, .. }| {
520  ///       matches.push((id, unsafe { source.as_str() }));
521  ///       MatchResult::Continue
522  ///     })?;
523  /// assert_eq!(&matches, &[(1, "fr\0e\0e")]);
524  /// # Ok(())
525  /// # }
526  /// ```
527  pub fn compile_multi_literal(
528    literal_set: &LiteralSet,
529    mode: Mode,
530    platform: Option<&Platform>,
531  ) -> Result<Self, VectorscanCompileError> {
532    let mut db = ptr::null_mut();
533    let mut compile_err = ptr::null_mut();
534    let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
535    VectorscanRuntimeError::copy_from_native_compile_error(
536      unsafe {
537        hs::hs_compile_lit_multi(
538          literal_set.literals_ptr(),
539          literal_set.flags_ptr(),
540          literal_set.ids_ptr(),
541          literal_set.lengths_ptr(),
542          literal_set.num_elements(),
543          mode.into_native(),
544          platform
545            .as_ref()
546            .map(|p| p as *const hs::hs_platform_info)
547            .unwrap_or(ptr::null()),
548          &mut db,
549          &mut compile_err,
550        )
551      },
552      compile_err,
553    )?;
554    Ok(unsafe { Self::from_native(db) })
555  }
556}
557
558/// # Introspection
559/// These methods extract various bits of runtime information from the db.
560impl Database {
561  /// Return the size of the db allocation.
562  ///
563  /// Using [`Flags::UCP`] explodes the size of character classes, which
564  /// increases the size of the state machine:
565  ///
566  ///```
567  /// #[cfg(feature = "compiler")]
568  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
569  ///   use vectorscan::{expression::*, flags::*};
570  ///
571  ///   let expr: Expression = r"\w".parse()?;
572  ///   let utf8_db = expr.compile(Flags::UTF8 | Flags::UCP, Mode::BLOCK)?;
573  ///   let ascii_db = expr.compile(Flags::default(), Mode::BLOCK)?;
574  ///
575  ///   // Including UTF-8 classes increases the size:
576  ///   assert!(utf8_db.database_size()? > ascii_db.database_size()?);
577  ///   Ok(())
578  /// }
579  /// # #[cfg(not(feature = "compiler"))]
580  /// # fn main() {}
581  /// ```
582  ///
583  /// This size corresponds to the requested allocation size passed to the db
584  /// allocator:
585  ///
586  ///```
587  /// #[cfg(all(feature = "alloc", feature = "compiler"))]
588  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
589  ///   use vectorscan::{expression::*, flags::*, alloc::*};
590  ///   use std::alloc::System;
591  ///
592  ///   // Wrap the standard Rust System allocator.
593  ///   let tracker = LayoutTracker::new(System.into());
594  ///   // Register it as the allocator for databases.
595  ///   assert!(set_db_allocator(tracker)?.is_none());
596  ///
597  ///   let expr: Expression = r"\w".parse()?;
598  ///   let utf8_db = expr.compile(Flags::UTF8 | Flags::UCP, Mode::BLOCK)?;
599  ///
600  ///   // Get the database allocator we just registered and view its live allocations:
601  ///   let allocs = get_db_allocator().as_ref().unwrap().current_allocations();
602  ///   // Verify that only the single known db was allocated:
603  ///   assert_eq!(1, allocs.len());
604  ///   let (_p, layout) = allocs[0];
605  ///
606  ///   // Verify that the allocation size is the same as reported:
607  ///   assert_eq!(layout.size(), utf8_db.database_size()?);
608  ///   Ok(())
609  /// }
610  /// # #[cfg(not(all(feature = "alloc", feature = "compiler")))]
611  /// # fn main() {}
612  /// ```
613  pub fn database_size(&self) -> Result<usize, VectorscanRuntimeError> {
614    let mut ret: MaybeUninit<usize> = MaybeUninit::uninit();
615    VectorscanRuntimeError::from_native(unsafe {
616      hs::hs_database_size(self.as_ref_native(), ret.as_mut_ptr())
617    })?;
618    Ok(unsafe { ret.assume_init() })
619  }
620
621  /// Return the amount of space necessary to maintain stream state for this db.
622  ///
623  ///```
624  /// #[cfg(feature = "compiler")]
625  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
626  ///   use vectorscan::{expression::*, flags::*};
627  ///
628  ///   let expr: Expression = r"\w".parse()?;
629  ///   let utf8_db = expr.compile(Flags::UTF8 | Flags::UCP, Mode::STREAM)?;
630  ///   let ascii_db = expr.compile(Flags::default(), Mode::STREAM)?;
631  ///
632  ///   // Including UTF-8 classes increases both db and stream size:
633  ///   assert!(utf8_db.database_size()? > ascii_db.database_size()?);
634  ///   assert!(utf8_db.stream_size()? > ascii_db.stream_size()?);
635  ///   Ok(())
636  /// }
637  /// # #[cfg(not(feature = "compiler"))]
638  /// # fn main() {}
639  /// ```
640  ///
641  /// This size corresponds to the requested allocation size passed to the
642  /// stream allocator:
643  ///
644  ///```
645  /// #[cfg(all(feature = "alloc", feature = "compiler"))]
646  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
647  ///   use vectorscan::{expression::*, flags::*, alloc::*, stream::*};
648  ///   use std::alloc::System;
649  ///
650  ///   // Wrap the standard Rust System allocator.
651  ///   let tracker = LayoutTracker::new(System.into());
652  ///   // Register it as the allocator for streams.
653  ///   assert!(set_stream_allocator(tracker)?.is_none());
654  ///
655  ///   let expr: Expression = r"\w".parse()?;
656  ///   let db = expr.compile(Flags::UTF8 | Flags::UCP, Mode::STREAM)?;
657  ///   let _stream = LiveStream::open(&db)?;
658  ///
659  ///   // Get the stream allocator we just registered and view its live allocations:
660  ///   let allocs = get_stream_allocator().as_ref().unwrap().current_allocations();
661  ///   // Verify that only the single known stream was allocated:
662  ///   assert_eq!(1, allocs.len());
663  ///   let (_p, layout) = allocs[0];
664  ///
665  ///   // Verify that the allocation size is the same as reported:
666  ///   assert_eq!(layout.size(), db.stream_size()?);
667  ///   Ok(())
668  /// }
669  /// # #[cfg(not(all(feature = "alloc", feature = "compiler")))]
670  /// # fn main() {}
671  /// ```
672  #[cfg(feature = "stream")]
673  #[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
674  pub fn stream_size(&self) -> Result<usize, VectorscanRuntimeError> {
675    let mut ret: MaybeUninit<usize> = MaybeUninit::uninit();
676    VectorscanRuntimeError::from_native(unsafe {
677      hs::hs_stream_size(self.as_ref_native(), ret.as_mut_ptr())
678    })?;
679    Ok(unsafe { ret.assume_init() })
680  }
681
682  /// Extract metadata about the current database into a new string allocation.
683  ///
684  /// This is a convenience method that simply calls
685  /// [`DbInfo::extract_db_info()`].
686  ///
687  ///```
688  /// #[cfg(feature = "compiler")]
689  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
690  ///   use vectorscan::{expression::*, flags::*};
691  ///
692  ///   let expr: Expression = "a+".parse()?;
693  ///   let db = expr.compile(Flags::default(), Mode::BLOCK)?;
694  ///   let info = db.info()?;
695  ///   assert_eq!(info.as_str(), "Version: 5.4.11 Features: AVX2 Mode: BLOCK");
696  ///   Ok(())
697  /// }
698  /// # #[cfg(not(feature = "compiler"))]
699  /// # fn main() {}
700  /// ```
701  pub fn info(&self) -> Result<DbInfo, VectorscanRuntimeError> { DbInfo::extract_db_info(self) }
702}
703
704/// # Managing Allocations
705/// These methods provide access to the underlying memory allocation containing
706/// the data for the in-memory state machine. They can be used along with
707/// [`SerializedDb::deserialize_db_at()`] to control the memory location used
708/// for the state machine, or to preserve db allocations across weird lifetime
709/// constraints.
710///
711/// Note that [`Self::database_size()`] can be used to determine the size of the
712/// memory allocation pointed to by [`Self::as_ref_native()`] and
713/// [`Self::as_mut_native()`].
714impl Database {
715  /// Wrap the provided allocation `p`.
716  ///
717  /// # Safety
718  /// The pointer `p` must point to an initialized db allocation prepared by one
719  /// of the compile or deserialize methods.
720  ///
721  /// This method also makes it especially easy to create multiple references to
722  /// the same allocation, which will then cause a double free when
723  /// [`Self::try_drop()`] is called more than once for the same db allocation.
724  /// To avoid this, wrap the result in a [`ManuallyDrop`](mem::ManuallyDrop):
725  ///
726  ///```
727  /// #[cfg(feature = "compiler")]
728  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
729  ///   use vectorscan::{expression::*, flags::*, matchers::*, database::*, state::*};
730  ///   use std::mem::ManuallyDrop;
731  ///
732  ///   // Compile a legitimate db:
733  ///   let expr: Expression = "a+".parse()?;
734  ///   let mut db = expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?;
735  ///
736  ///   // Create two new references to that allocation,
737  ///   // wrapped to avoid calling the drop code:
738  ///   let db_ptr: *mut NativeDb = db.as_mut_native();
739  ///   let db_ref_1 = ManuallyDrop::new(unsafe { Database::from_native(db_ptr) });
740  ///   let db_ref_2 = ManuallyDrop::new(unsafe { Database::from_native(db_ptr) });
741  ///
742  ///   // Both db references are valid and can be used for matching.
743  ///   let mut scratch = Scratch::blank();
744  ///   scratch.setup_for_db(&db_ref_1)?;
745  ///   scratch.setup_for_db(&db_ref_2)?;
746  ///
747  ///   let mut matches: Vec<&str> = Vec::new();
748  ///   scratch
749  ///     .scan_sync(&db_ref_1, "aardvark".into(), |Match { source, .. }| {
750  ///       matches.push(unsafe { source.as_str() });
751  ///       MatchResult::Continue
752  ///     })?;
753  ///   scratch
754  ///     .scan_sync(&db_ref_2, "aardvark".into(), |Match { source, .. }| {
755  ///       matches.push(unsafe { source.as_str() });
756  ///       MatchResult::Continue
757  ///     })?;
758  ///   assert_eq!(&matches, &["a", "aa", "a", "a", "aa", "a"]);
759  ///   Ok(())
760  /// }
761  /// # #[cfg(not(feature = "compiler"))]
762  /// # fn main() {}
763  /// ```
764  pub const unsafe fn from_native(p: *mut NativeDb) -> Self { Self(p) }
765
766  /// Get a read-only reference to the db allocation.
767  ///
768  /// This method is mostly used internally and cast to a pointer to provide to
769  /// the vectorscan native library methods.
770  pub fn as_ref_native(&self) -> &NativeDb { unsafe { &*self.0 } }
771
772  /// Get a mutable reference to the db allocation.
773  ///
774  /// The result of this method can be cast to a pointer and provided to
775  /// [`Self::from_native()`].
776  pub fn as_mut_native(&mut self) -> &mut NativeDb { unsafe { &mut *self.0 } }
777
778  /// Free the underlying db allocation.
779  ///
780  /// # Safety
781  /// This method must be called at most once over the lifetime of each db
782  /// allocation. It is called by default on drop, so
783  /// [`ManuallyDrop`](mem::ManuallyDrop) is recommended to wrap instances
784  /// that reference external data in order to avoid attempting to free the
785  /// referenced data.
786  ///
787  /// ## Only Frees Memory
788  /// This method performs no processing other than freeing the allocated
789  /// memory, so it can be skipped without leaking resources if the
790  /// underlying [`NativeDb`] allocation is freed by some other means.
791  pub unsafe fn try_drop(&mut self) -> Result<(), VectorscanRuntimeError> {
792    VectorscanRuntimeError::from_native(unsafe { hs::hs_free_database(self.as_mut_native()) })
793  }
794}
795
796impl ops::Drop for Database {
797  fn drop(&mut self) {
798    unsafe {
799      self.try_drop().unwrap();
800    }
801  }
802}
803
804unsafe impl Send for Database {}
805
806/// Wrappers over allocations from various sources.
807///
808/// In particular, this module contains [`DbAllocation`](alloc::DbAllocation),
809/// which provides the logic needed to abstract over different sources of
810/// backing data used to contain a [`SerializedDb`].
811pub mod alloc {
812  use std::{borrow::Cow, ops, slice};
813
814  /// An allocation of memory using the misc allocator.
815  ///
816  /// The allocator used for this memory can be modified or accessed with
817  /// [`crate::alloc::set_misc_allocator()`] and
818  /// [`crate::alloc::get_misc_allocator()`] if the `"alloc"` feature is
819  /// enabled.
820  ///
821  /// The backing memory will be deallocated by the misc allocator upon drop
822  /// unless this is wrapped with a [`ManuallyDrop`](std::mem::ManuallyDrop).
823  #[derive(Debug)]
824  pub struct MiscAllocation {
825    pub(crate) data: *mut u8,
826    pub(crate) len: usize,
827  }
828
829  unsafe impl Send for MiscAllocation {}
830
831  impl MiscAllocation {
832    pub(crate) const fn as_ptr(&self) -> *mut u8 { self.data }
833
834    pub(crate) const fn len(&self) -> usize { self.len }
835
836    /// Return a view over the backing memory region.
837    pub const fn as_slice(&self) -> &[u8] {
838      unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
839    }
840
841    unsafe fn free(&mut self) { crate::free_misc(self.data) }
842  }
843
844  impl ops::Drop for MiscAllocation {
845    fn drop(&mut self) {
846      unsafe {
847        self.free();
848      }
849    }
850  }
851
852  /// Wrapper over a misc or rust-level allocation.
853  ///
854  /// Used to provide [`super::SerializedDb`] with the ability to source data
855  /// allocated by the vectorscan library itself or by other Rust code.
856  #[derive(Debug)]
857  pub enum DbAllocation<'a> {
858    /// Memory was allocated with a `'static` lifetime using the registered misc
859    /// allocator.
860    Misc(MiscAllocation),
861    /// Memory was allocated with a known lifetime and may be owned or
862    /// referenced.
863    Rust(Cow<'a, [u8]>),
864  }
865
866  /// Methods available to all types of allocations.
867  impl<'a> DbAllocation<'a> {
868    pub(crate) fn as_ptr(&self) -> *const u8 {
869      match self {
870        Self::Misc(misc) => misc.as_ptr(),
871        Self::Rust(cow) => cow.as_ptr(),
872      }
873    }
874
875    pub(crate) fn len(&self) -> usize {
876      match self {
877        Self::Misc(misc) => misc.len(),
878        Self::Rust(cow) => cow.len(),
879      }
880    }
881
882    /// Return a view over the backing memory region, wherever it may come from.
883    pub fn as_slice(&self) -> &[u8] { unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) } }
884  }
885
886  /// Methods that produce new owned (`'static`) allocations.
887  ///
888  /// A [`Clone`] impl is also available for such owned allocations.
889  impl DbAllocation<'static> {
890    /// Copy the referenced data into a new Rust-level`'static` allocation.
891    pub fn from_cloned_data(s: &DbAllocation) -> Self {
892      let newly_allocated: Vec<u8> = s.as_slice().to_vec();
893      Self::Rust(Cow::Owned(newly_allocated))
894    }
895  }
896
897  impl Clone for DbAllocation<'static> {
898    fn clone(&self) -> Self { Self::from_cloned_data(self) }
899  }
900
901  /// Wrappers over allocations performed by the chimera library.
902  ///
903  /// Since chimera does not support database deserialization like the base
904  /// vectorscan library, there is no analogy to [`DbAllocation`].
905  #[cfg(feature = "chimera")]
906  #[cfg_attr(docsrs, doc(cfg(feature = "chimera")))]
907  pub mod chimera {
908    use std::{ops, slice};
909
910    /// An allocation of memory using the chimera misc allocator.
911    ///
912    /// The allocator used for this memory can be modified or accessed with
913    /// [`crate::alloc::chimera::set_chimera_misc_allocator()`] and
914    /// [`crate::alloc::chimera::get_chimera_misc_allocator()`] if the `"alloc"`
915    /// feature is enabled.
916    ///
917    /// The backing memory will be deallocated by the chimera misc allocator
918    /// upon drop unless this is wrapped with a
919    /// [`ManuallyDrop`](std::mem::ManuallyDrop).
920    #[derive(Debug)]
921    pub struct ChimeraMiscAllocation {
922      pub(crate) data: *mut u8,
923      pub(crate) len: usize,
924    }
925
926    unsafe impl Send for ChimeraMiscAllocation {}
927
928    impl ChimeraMiscAllocation {
929      pub(crate) const fn as_ptr(&self) -> *mut u8 { self.data }
930
931      pub(crate) const fn len(&self) -> usize { self.len }
932
933      /// Return a view over the backing memory region.
934      pub const fn as_slice(&self) -> &[u8] {
935        unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) }
936      }
937
938      unsafe fn free(&mut self) { crate::free_misc_chimera(self.data) }
939    }
940
941    impl ops::Drop for ChimeraMiscAllocation {
942      fn drop(&mut self) {
943        unsafe {
944          self.free();
945        }
946      }
947    }
948  }
949}
950
951/// Wrapper for allocated string data returned by [`Database::info()`].
952#[repr(transparent)]
953pub struct DbInfo(pub alloc::MiscAllocation);
954
955impl DbInfo {
956  const fn without_null(&self) -> impl slice::SliceIndex<[u8], Output=[u8]> { ..(self.0.len() - 1) }
957
958  /// Return a view of the allocated string data.
959  ///
960  /// Vectorscan will always return valid UTF-8 data for this string, so it
961  /// skips the validity check. Note that the returned string does not include
962  /// the trailing null byte allocated by the underlying vectorscan library.
963  pub fn as_str(&self) -> &str {
964    unsafe { str::from_utf8_unchecked(&self.0.as_slice()[self.without_null()]) }
965  }
966
967  /// Write out metadata for `db` into a newly allocated region.
968  pub fn extract_db_info(db: &Database) -> Result<Self, VectorscanRuntimeError> {
969    let mut info = ptr::null_mut();
970    VectorscanRuntimeError::from_native(unsafe {
971      hs::hs_database_info(db.as_ref_native(), &mut info)
972    })?;
973    let len = unsafe { CStr::from_ptr(info) }.to_bytes_with_nul().len();
974    assert!(len > 0);
975
976    let ret = alloc::MiscAllocation {
977      data: unsafe { mem::transmute(info) },
978      len,
979    };
980
981    Ok(Self(ret))
982  }
983}
984
985impl fmt::Debug for DbInfo {
986  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DbInfo({:?})", self.as_str()) }
987}
988
989impl fmt::Display for DbInfo {
990  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_str()) }
991}
992
993impl cmp::PartialEq for DbInfo {
994  fn eq(&self, other: &Self) -> bool { self.as_str().eq(other.as_str()) }
995}
996
997impl cmp::Eq for DbInfo {}
998
999impl cmp::PartialOrd for DbInfo {
1000  fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
1001}
1002
1003impl cmp::Ord for DbInfo {
1004  fn cmp(&self, other: &Self) -> cmp::Ordering { self.as_str().cmp(other.as_str()) }
1005}
1006
1007impl hash::Hash for DbInfo {
1008  fn hash<H>(&self, state: &mut H)
1009  where H: hash::Hasher {
1010    self.as_str().hash(state);
1011  }
1012}
1013
1014/// Wrapper for a serialized form of a [`Database`].
1015#[derive(Debug)]
1016#[repr(transparent)]
1017pub struct SerializedDb<'a>(
1018  /// This serialization data can be sourced from a variety of places.
1019  pub alloc::DbAllocation<'a>,
1020);
1021
1022/// Methods available to all types of allocations.
1023impl<'a> SerializedDb<'a> {
1024  fn as_ptr(&self) -> *const c_char { unsafe { mem::transmute(self.0.as_ptr()) } }
1025
1026  fn len(&self) -> usize { self.0.len() }
1027
1028  /// Deserialize into a new db allocation.
1029  ///
1030  /// This will make a new allocation through the allocator from
1031  /// [`crate::alloc::set_db_allocator()`].
1032  ///
1033  ///```
1034  /// #[cfg(feature = "compiler")]
1035  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
1036  ///   use vectorscan::{expression::*, flags::*};
1037  ///
1038  ///   let expr: Expression = "a+".parse()?;
1039  ///   let serialized_db = expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?.serialize()?;
1040  ///   let db = serialized_db.deserialize_db()?;
1041  ///
1042  ///   // Note that the expected deserialized size is the same
1043  ///   // as the resulting in-memory database size:
1044  ///   assert_eq!(db.database_size()?, serialized_db.deserialized_size()?);
1045  ///   Ok(())
1046  /// }
1047  /// # #[cfg(not(feature = "compiler"))]
1048  /// # fn main() {}
1049  /// ```
1050  pub fn deserialize_db(&self) -> Result<Database, VectorscanRuntimeError> {
1051    let mut deserialized: MaybeUninit<*mut hs::hs_database> = MaybeUninit::uninit();
1052    VectorscanRuntimeError::from_native(unsafe {
1053      hs::hs_deserialize_database(self.as_ptr(), self.len(), deserialized.as_mut_ptr())
1054    })?;
1055    let deserialized = unsafe { deserialized.assume_init() };
1056    Ok(unsafe { Database::from_native(deserialized) })
1057  }
1058
1059  /// Return the size of the allocation necessary for a subsequent call to
1060  /// [`Self::deserialize_db_at()`].
1061  pub fn deserialized_size(&self) -> Result<usize, VectorscanRuntimeError> {
1062    let mut deserialized_size: MaybeUninit<usize> = MaybeUninit::uninit();
1063    VectorscanRuntimeError::from_native(unsafe {
1064      hs::hs_serialized_database_size(self.as_ptr(), self.len(), deserialized_size.as_mut_ptr())
1065    })?;
1066    let deserialized_size = unsafe { deserialized_size.assume_init() };
1067    Ok(deserialized_size)
1068  }
1069
1070  /// Like [`Self::deserialize_db()`], but points into an existing allocation
1071  /// instead of making a new allocation.
1072  ///
1073  /// # Safety
1074  /// `db` must point to an allocation at least
1075  /// [`Self::deserialized_size()`] bytes in size!
1076  ///
1077  ///```
1078  /// #[cfg(feature = "compiler")]
1079  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
1080  ///   use vectorscan::{expression::*, flags::*, database::*};
1081  ///   use std::mem;
1082  ///
1083  ///   let expr: Expression = "a+".parse()?;
1084  ///   let serialized_db = expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?.serialize()?;
1085  ///
1086  ///   // Allocate a vector with sufficient capacity for the deserialized db:
1087  ///   let mut db_data: Vec<u8> = Vec::with_capacity(serialized_db.deserialized_size()?);
1088  ///   let db = unsafe {
1089  ///     let db_ptr: *mut NativeDb = mem::transmute(db_data.as_mut_ptr());
1090  ///     serialized_db.deserialize_db_at(db_ptr)?;
1091  ///     // Wrap in ManuallyDrop to avoid freeing memory owned by the `db_data` vector.
1092  ///     mem::ManuallyDrop::new(Database::from_native(db_ptr))
1093  ///   };
1094  ///   // Note that the expected deserialized size is the same
1095  ///   // as the resulting in-memory database size:
1096  ///   assert_eq!(db.database_size()?, serialized_db.deserialized_size()?);
1097  ///   Ok(())
1098  /// }
1099  /// # #[cfg(not(feature = "compiler"))]
1100  /// # fn main() {}
1101  /// ```
1102  pub unsafe fn deserialize_db_at(&self, db: *mut NativeDb) -> Result<(), VectorscanRuntimeError> {
1103    VectorscanRuntimeError::from_native(hs::hs_deserialize_database_at(
1104      self.as_ptr(),
1105      self.len(),
1106      db,
1107    ))
1108  }
1109
1110  /// Extract metadata about the serialized database into a new string
1111  /// allocation.
1112  ///
1113  ///```
1114  /// #[cfg(feature = "compiler")]
1115  /// fn main() -> Result<(), vectorscan::error::VectorscanError> {
1116  ///   use vectorscan::{expression::*, flags::*};
1117  ///
1118  ///   let expr: Expression = "a+".parse()?;
1119  ///   let serialized_db = expr.compile(Flags::default(), Mode::BLOCK)?.serialize()?;
1120  ///   let info = serialized_db.extract_db_info()?;
1121  ///   assert_eq!(info.as_str(), "Version: 5.4.11 Features: AVX2 Mode: BLOCK");
1122  ///   // Info is the same as would have been provided from deserializing:
1123  ///   assert_eq!(info, serialized_db.deserialize_db()?.info()?);
1124  ///   Ok(())
1125  /// }
1126  /// # #[cfg(not(feature = "compiler"))]
1127  /// # fn main() {}
1128  /// ```
1129  pub fn extract_db_info(&self) -> Result<DbInfo, VectorscanRuntimeError> {
1130    let mut info = ptr::null_mut();
1131    VectorscanRuntimeError::from_native(unsafe {
1132      hs::hs_serialized_database_info(self.as_ptr(), self.len(), &mut info)
1133    })?;
1134    let len = unsafe { CStr::from_ptr(info) }.to_bytes_with_nul().len();
1135    assert!(len > 0);
1136
1137    let ret = alloc::MiscAllocation {
1138      data: info as *mut u8,
1139      len,
1140    };
1141
1142    Ok(DbInfo(ret))
1143  }
1144}
1145
1146/// # Owned Allocations
1147/// Methods that produce new owned (`'static`) allocations.
1148///
1149/// A [`Clone`] impl is also available for such owned allocations.
1150impl SerializedDb<'static> {
1151  /// Write a serialized representation of `db` into a newly allocated region of
1152  /// memory.
1153  pub fn serialize_db(db: &Database) -> Result<Self, VectorscanRuntimeError> {
1154    let mut data = ptr::null_mut();
1155    let mut len: usize = 0;
1156
1157    VectorscanRuntimeError::from_native(unsafe {
1158      hs::hs_serialize_database(db.as_ref_native(), &mut data, &mut len)
1159    })?;
1160
1161    let data = data as *mut u8;
1162
1163    Ok(Self(alloc::DbAllocation::Misc(alloc::MiscAllocation {
1164      data,
1165      len,
1166    })))
1167  }
1168
1169  /// Allocate a new region of memory and copy over the referenced data from
1170  /// `s`.
1171  pub fn from_cloned_data(s: &SerializedDb) -> Self {
1172    let SerializedDb(ref s) = s;
1173    Self(alloc::DbAllocation::from_cloned_data(s))
1174  }
1175}
1176
1177impl Clone for SerializedDb<'static> {
1178  fn clone(&self) -> Self { Self::from_cloned_data(self) }
1179}
1180
1181/// Compile chimera state machines from expressions.
1182///
1183/// Unlike the base vectorscan library, chimera does not support database
1184/// serialization, so new [`chimera::ChimeraDb`] instances can only be created
1185/// by compiling them. That is why the `"chimera"` feature for this crate
1186/// requires the `"compiler"` feature.
1187#[cfg(feature = "chimera")]
1188#[cfg_attr(docsrs, doc(cfg(feature = "chimera")))]
1189pub mod chimera {
1190  use super::alloc::chimera::ChimeraMiscAllocation;
1191  #[cfg(feature = "compiler")]
1192  use super::Platform;
1193  #[cfg(feature = "compiler")]
1194  use crate::{
1195    error::chimera::ChimeraCompileError,
1196    expression::chimera::{ChimeraExpression, ChimeraExpressionSet, ChimeraMatchLimits},
1197    flags::chimera::{ChimeraFlags, ChimeraMode},
1198  };
1199  use crate::{error::chimera::ChimeraRuntimeError, hs, state::chimera::ChimeraScratch};
1200
1201  use std::{cmp, ffi::CStr, fmt, hash, mem, ops, ptr, slice, str};
1202
1203  /// Pointer type for chimera db allocations used in [`ChimeraDb#Managing
1204  /// Allocations`](ChimeraDb#managing-allocations).
1205  pub type NativeChimeraDb = hs::ch_database;
1206
1207  /// Read-only description of an in-memory PCRE state machine.
1208  ///
1209  /// This type also serves as the entry point to the various types of [pattern
1210  /// compilers](#pattern-compilers), including single expressions and
1211  /// expression sets.
1212  #[derive(Debug)]
1213  #[repr(transparent)]
1214  pub struct ChimeraDb(*mut NativeChimeraDb);
1215
1216  /// # Convenience Methods
1217  /// These methods prepare some resource within a new heap allocation and are
1218  /// useful for doctests and examples.
1219  ///
1220  /// ## Scratch Setup
1221  /// Databases already require their own heap allocation, which can be managed
1222  /// with the methods in [Managing Allocations](#managing-allocations).
1223  /// However, databases also impose a sort of implicit dynamic lifetime
1224  /// constraint on [`ChimeraScratch`] objects, which must be initialized
1225  /// against a db with [`ChimeraScratch::setup_for_db()`] before vectorscan
1226  /// can do any searching.
1227  ///
1228  /// It is encouraged to re-use [`ChimeraScratch`] objects across databases
1229  /// where possible to minimize unnecessary allocations, but
1230  /// [`Self::allocate_scratch()`] is provided as a convenience method to
1231  /// quickly produce a 1:1 db:scratch mapping.
1232  impl ChimeraDb {
1233    /// Call [`ChimeraScratch::setup_for_db()`] on a newly allocated
1234    /// [`ChimeraScratch::blank()`].
1235    ///
1236    ///```
1237    /// # fn main() -> Result<(), vectorscan::error::chimera::ChimeraError> {
1238    /// use vectorscan::{expression::chimera::*, flags::chimera::*, matchers::chimera::*};
1239    ///
1240    /// let expr: ChimeraExpression = "a+".parse()?;
1241    /// let db = expr.compile(ChimeraFlags::default(), ChimeraMode::NOGROUPS)?;
1242    /// let mut scratch = db.allocate_scratch()?;
1243    ///
1244    /// let mut matches: Vec<&str> = Vec::new();
1245    /// let e = |_| ChimeraMatchResult::Continue;
1246    /// scratch
1247    ///   .scan_sync(&db, "aardvark".into(), |ChimeraMatch { source, .. }| {
1248    ///     matches.push(unsafe { source.as_str() });
1249    ///     ChimeraMatchResult::Continue
1250    ///   }, e)?;
1251    /// assert_eq!(&matches, &["aa", "a"]);
1252    /// # Ok(())
1253    /// # }
1254    /// ```
1255    pub fn allocate_scratch(&self) -> Result<ChimeraScratch, ChimeraRuntimeError> {
1256      let mut scratch = ChimeraScratch::blank();
1257      scratch.setup_for_db(self)?;
1258      Ok(scratch)
1259    }
1260  }
1261
1262  /// # Pattern Compilers
1263  /// Chimera supports compiling state machines for single PCRE pattern strings
1264  /// as well as parallel sets of those patterns. Each compile method currently
1265  /// supports all [`ChimeraFlags`] arguments.
1266  ///
1267  /// ## Platform Compatibility
1268  /// Each method also accepts an optional [`Platform`] object,
1269  /// which is used to select processor features to compile the database for.
1270  /// While the default of [`None`] will enable all features available to the
1271  /// current processor, some features can be disabled in order to produce a
1272  /// database which can execute on a wider variety of target platforms.
1273  /// **However, note that since chimera does not support deserialization like
1274  /// the base vectorscan library, there currently seems to be no real benefit
1275  /// to a more-generic but less-performant compiled database, so using
1276  /// [`None`] is recommended in all cases.**
1277  ///
1278  ///```
1279  /// # fn main() -> Result<(), vectorscan::error::chimera::ChimeraError> {
1280  /// use vectorscan::{expression::chimera::*, flags::{*, chimera::*, platform::*}, database::chimera::*};
1281  ///
1282  /// let expr: ChimeraExpression = "a+".parse()?;
1283  ///
1284  /// // Verify that the current platform has AVX2 instructions, and make a db:
1285  /// let plat = Platform::local()?;
1286  /// assert!(plat.cpu_features.contains(&CpuFeatures::AVX2));
1287  /// assert!(plat != Platform::GENERIC);
1288  /// let db_with_avx2 = ChimeraDb::compile(
1289  ///   &expr,
1290  ///   ChimeraFlags::default(),
1291  ///   ChimeraMode::NOGROUPS,
1292  ///   Some(&plat),
1293  /// )?;
1294  ///
1295  /// // The only specialized instructions we have available are AVX2:
1296  /// assert_eq!(CpuFeatures::NONE, plat.cpu_features & !CpuFeatures::AVX2);
1297  /// // Avoid using AVX2 instructions:
1298  /// let db_no_avx2 = ChimeraDb::compile(
1299  ///   &expr,
1300  ///   ChimeraFlags::default(),
1301  ///   ChimeraMode::NOGROUPS,
1302  ///   Some(&Platform::GENERIC),
1303  /// )?;
1304  ///
1305  /// // Instruction selection does not affect the size of the state machine:
1306  /// assert!(db_with_avx2.database_size()? == db_no_avx2.database_size()?);
1307  ///
1308  /// // Now create a db with None for the platform:
1309  /// let db_local = ChimeraDb::compile(
1310  ///   &expr,
1311  ///   ChimeraFlags::default(),
1312  ///   ChimeraMode::NOGROUPS,
1313  ///   None,
1314  /// )?;
1315  /// assert!(db_with_avx2.database_size()? == db_local.database_size()?);
1316  ///
1317  /// // Using None produces the same type of db as Platform::local():
1318  /// assert!(db_with_avx2.info()? == db_local.info()?);
1319  /// assert!(db_no_avx2.info()? != db_local.info()?);
1320  /// # Ok(())
1321  /// # }
1322  /// ```
1323  ///
1324  /// ## Dynamic Memory Allocation
1325  /// These methods allocate a new region of memory using the db allocator
1326  /// (which can be overridden with
1327  /// [`crate::alloc::chimera::set_chimera_db_allocator()`]). That allocation
1328  /// can be manipulated as described in [Managing Allocations](#
1329  /// managing-allocations).
1330  #[cfg(feature = "compiler")]
1331  #[cfg_attr(docsrs, doc(cfg(feature = "compiler")))]
1332  impl ChimeraDb {
1333    /// Single pattern compiler.
1334    ///
1335    /// # Accepted Flags
1336    /// - [`CASELESS`](ChimeraFlags::CASELESS)
1337    /// - [`DOTALL`](ChimeraFlags::DOTALL)
1338    /// - [`MULTILINE`](ChimeraFlags::MULTILINE)
1339    /// - [`SINGLEMATCH`](ChimeraFlags::SINGLEMATCH)
1340    /// - [`UTF8`](ChimeraFlags::UTF8)
1341    /// - [`UCP`](ChimeraFlags::UCP)
1342    ///
1343    ///```
1344    /// # fn main() -> Result<(), vectorscan::error::chimera::ChimeraError> {
1345    /// use vectorscan::{expression::chimera::*, flags::chimera::*, database::chimera::*, matchers::chimera::*};
1346    ///
1347    /// let expr: ChimeraExpression = "hell(o)?".parse()?;
1348    /// let db = ChimeraDb::compile(&expr, ChimeraFlags::default(), ChimeraMode::GROUPS, None)?;
1349    ///
1350    /// let mut scratch = db.allocate_scratch()?;
1351    ///
1352    /// let mut matches: Vec<(&str, Option<&str>)> = Vec::new();
1353    /// let e = |_| ChimeraMatchResult::Continue;
1354    /// scratch
1355    ///   .scan_sync(&db, "hello".into(), |m| {
1356    ///     matches.push(unsafe { (
1357    ///       m.source.as_str(),
1358    ///       m.captures.unwrap()[1].map(|c| c.as_str()),
1359    ///     ) });
1360    ///     ChimeraMatchResult::Continue
1361    ///   }, e)?;
1362    /// assert_eq!(&matches, &[("hello", Some("o"))]);
1363    /// # Ok(())
1364    /// # }
1365    /// ```
1366    pub fn compile(
1367      expression: &ChimeraExpression,
1368      flags: ChimeraFlags,
1369      mode: ChimeraMode,
1370      platform: Option<&Platform>,
1371    ) -> Result<Self, ChimeraCompileError> {
1372      let mut db = ptr::null_mut();
1373      let mut compile_err = ptr::null_mut();
1374      let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
1375      ChimeraRuntimeError::copy_from_native_compile_error(
1376        unsafe {
1377          hs::ch_compile(
1378            expression.as_ptr(),
1379            flags.into_native(),
1380            mode.into_native(),
1381            platform
1382              .as_ref()
1383              .map(|p| p as *const hs::hs_platform_info)
1384              .unwrap_or(ptr::null()),
1385            &mut db,
1386            &mut compile_err,
1387          )
1388        },
1389        compile_err,
1390      )?;
1391      Ok(unsafe { Self::from_native(db) })
1392    }
1393
1394    /// Multiple pattern compiler.
1395    ///
1396    /// # Accepted Flags
1397    /// - [`CASELESS`](ChimeraFlags::CASELESS)
1398    /// - [`DOTALL`](ChimeraFlags::DOTALL)
1399    /// - [`MULTILINE`](ChimeraFlags::MULTILINE)
1400    /// - [`SINGLEMATCH`](ChimeraFlags::SINGLEMATCH)
1401    /// - [`UTF8`](ChimeraFlags::UTF8)
1402    /// - [`UCP`](ChimeraFlags::UCP)
1403    ///
1404    ///```
1405    /// # fn main() -> Result<(), vectorscan::error::chimera::ChimeraError> {
1406    /// use vectorscan::{expression::{*, chimera::*}, flags::chimera::*, database::chimera::*, matchers::chimera::*};
1407    ///
1408    /// let a_expr: ChimeraExpression = "a+".parse()?;
1409    /// let b_expr: ChimeraExpression = "b+".parse()?;
1410    /// let exprs = ChimeraExpressionSet::from_exprs([&a_expr, &b_expr])
1411    ///   .with_ids([ExprId(1), ExprId(2)])
1412    ///   .with_limits(ChimeraMatchLimits { match_limit: 30, match_limit_recursion: 30 });
1413    /// let db = ChimeraDb::compile_multi(&exprs, ChimeraMode::NOGROUPS, None)?;
1414    /// let mut scratch = db.allocate_scratch()?;
1415    ///
1416    /// let mut matches: Vec<&str> = Vec::new();
1417    /// let e = |_| ChimeraMatchResult::Continue;
1418    /// scratch.scan_sync(&db, "aardvark imbibbe".into(), |ChimeraMatch { source, .. }| {
1419    ///    matches.push(unsafe { source.as_str() });
1420    ///    ChimeraMatchResult::Continue
1421    ///  }, e)?;
1422    /// assert_eq!(&matches, &["aa", "a", "b", "bb"]);
1423    /// # Ok(())
1424    /// # }
1425    /// ```
1426    pub fn compile_multi(
1427      exprs: &ChimeraExpressionSet,
1428      mode: ChimeraMode,
1429      platform: Option<&Platform>,
1430    ) -> Result<Self, ChimeraCompileError> {
1431      let mut db = ptr::null_mut();
1432      let mut compile_err = ptr::null_mut();
1433      let platform: Option<hs::hs_platform_info> = platform.cloned().map(Platform::into_native);
1434      ChimeraRuntimeError::copy_from_native_compile_error(
1435        unsafe {
1436          if let Some(ChimeraMatchLimits {
1437            match_limit,
1438            match_limit_recursion,
1439          }) = exprs.limits()
1440          {
1441            hs::ch_compile_ext_multi(
1442              exprs.expressions_ptr(),
1443              exprs.flags_ptr(),
1444              exprs.ids_ptr(),
1445              exprs.num_elements(),
1446              mode.into_native(),
1447              match_limit,
1448              match_limit_recursion,
1449              platform
1450                .as_ref()
1451                .map(|p| p as *const hs::hs_platform_info)
1452                .unwrap_or(ptr::null()),
1453              &mut db,
1454              &mut compile_err,
1455            )
1456          } else {
1457            hs::ch_compile_multi(
1458              exprs.expressions_ptr(),
1459              exprs.flags_ptr(),
1460              exprs.ids_ptr(),
1461              exprs.num_elements(),
1462              mode.into_native(),
1463              platform
1464                .as_ref()
1465                .map(|p| p as *const hs::hs_platform_info)
1466                .unwrap_or(ptr::null()),
1467              &mut db,
1468              &mut compile_err,
1469            )
1470          }
1471        },
1472        compile_err,
1473      )?;
1474      Ok(unsafe { Self::from_native(db) })
1475    }
1476  }
1477
1478  /// # Introspection
1479  /// These methods extract various bits of runtime information from the db.
1480  impl ChimeraDb {
1481    /// Return the size of the db allocation.
1482    ///
1483    /// Using [`ChimeraFlags::UCP`] explodes the size of character classes,
1484    /// which increases the size of the state machine:
1485    ///
1486    ///```
1487    /// # fn main() -> Result<(), vectorscan::error::chimera::ChimeraError> {
1488    /// use vectorscan::{expression::chimera::*, flags::chimera::*};
1489    ///
1490    /// let expr: ChimeraExpression = r"\w".parse()?;
1491    /// let utf8_db = expr.compile(
1492    ///   ChimeraFlags::UTF8 | ChimeraFlags::UCP,
1493    ///   ChimeraMode::NOGROUPS,
1494    /// )?;
1495    /// let ascii_db = expr.compile(ChimeraFlags::default(), ChimeraMode::NOGROUPS)?;
1496    ///
1497    /// // Including UTF-8 classes increases the size:
1498    /// assert!(utf8_db.database_size()? > ascii_db.database_size()?);
1499    /// # Ok(())
1500    /// # }
1501    /// ```
1502    ///
1503    /// This size corresponds to the requested allocation size passed to the db
1504    /// allocator:
1505    ///
1506    ///```
1507    /// #[cfg(feature = "alloc")]
1508    /// fn main() -> Result<(), vectorscan::error::chimera::ChimeraError> {
1509    ///   use vectorscan::{expression::chimera::*, flags::chimera::*, alloc::{*, chimera::*}};
1510    ///   use std::alloc::System;
1511    ///
1512    ///   // Wrap the standard Rust System allocator.
1513    ///   let tracker = LayoutTracker::new(System.into());
1514    ///   // Register it as the allocator for databases.
1515    ///   assert!(set_chimera_db_allocator(tracker)?.is_none());
1516    ///
1517    ///   let expr: ChimeraExpression = r"\w".parse()?;
1518    ///   let utf8_db = expr.compile(
1519    ///     ChimeraFlags::UTF8 | ChimeraFlags::UCP,
1520    ///     ChimeraMode::NOGROUPS,
1521    ///   )?;
1522    ///
1523    ///   // Get the database allocator we just registered and view its live allocations:
1524    ///   let allocs = get_chimera_db_allocator().as_ref().unwrap().current_allocations();
1525    ///   // Verify that only the single known db was allocated:
1526    ///   assert_eq!(1, allocs.len());
1527    ///   let (_p, layout) = allocs[0];
1528    ///
1529    ///   // Verify that the allocation size is the same as reported:
1530    ///   assert_eq!(layout.size(), utf8_db.database_size()?);
1531    ///   Ok(())
1532    /// }
1533    /// # #[cfg(not(feature = "alloc"))]
1534    /// # fn main() {}
1535    /// ```
1536    pub fn database_size(&self) -> Result<usize, ChimeraRuntimeError> {
1537      let mut database_size: usize = 0;
1538      ChimeraRuntimeError::from_native(unsafe {
1539        hs::ch_database_size(self.as_ref_native(), &mut database_size)
1540      })?;
1541      Ok(database_size)
1542    }
1543
1544    /// Extract metadata about the current database into a new string
1545    /// allocation.
1546    ///
1547    /// This is a convenience method that simply calls
1548    /// [`ChimeraDbInfo::extract_db_info()`].
1549    ///
1550    ///```
1551    /// # fn main() -> Result<(), vectorscan::error::chimera::ChimeraError> {
1552    /// use vectorscan::{expression::chimera::*, flags::chimera::*, database::chimera::*};
1553    ///
1554    /// let expr: ChimeraExpression = "a+".parse()?;
1555    /// let db = expr.compile(ChimeraFlags::default(), ChimeraMode::NOGROUPS)?;
1556    /// let info = ChimeraDbInfo::extract_db_info(&db)?;
1557    /// assert_eq!(info.as_str(), "Chimera Version: 5.4.11 Features: AVX2 Mode: BLOCK");
1558    /// # Ok(())
1559    /// # }
1560    /// ```
1561    pub fn info(&self) -> Result<ChimeraDbInfo, ChimeraRuntimeError> {
1562      ChimeraDbInfo::extract_db_info(self)
1563    }
1564  }
1565
1566  /// # Managing Allocations
1567  /// These methods provide access to the underlying memory allocation
1568  /// containing the data for the in-memory state machine. They can be used to
1569  /// control the memory location used for the state machine, or to preserve
1570  /// db allocations across weird lifetime constraints.
1571  ///
1572  /// Note that [`Self::database_size()`] can be used to determine the size of
1573  /// the memory allocation pointed to by [`Self::as_ref_native()`] and
1574  /// [`Self::as_mut_native()`].
1575  impl ChimeraDb {
1576    /// Wrap the provided allocation `p`.
1577    ///
1578    /// # Safety
1579    /// The pointer `p` must point to an initialized db allocation prepared by
1580    /// one of the compile methods.
1581    ///
1582    /// This method also makes it especially easy to create multiple references
1583    /// to the same allocation, which will then cause a double free when
1584    /// [`Self::try_drop()`] is called more than once for the same db
1585    /// allocation. To avoid this, wrap the result in a
1586    /// [`ManuallyDrop`](mem::ManuallyDrop):
1587    ///
1588    ///```
1589    /// # fn main() -> Result<(), vectorscan::error::chimera::ChimeraError> {
1590    /// use vectorscan::{expression::chimera::*, flags::chimera::*, matchers::chimera::*, database::chimera::*, state::chimera::*};
1591    /// use std::mem::ManuallyDrop;
1592    ///
1593    /// // Compile a legitimate db:
1594    /// let expr: ChimeraExpression = "a+".parse()?;
1595    /// let mut db = expr.compile(ChimeraFlags::default(), ChimeraMode::NOGROUPS)?;
1596    ///
1597    /// // Create two new references to that allocation,
1598    /// // wrapped to avoid calling the drop code:
1599    /// let db_ptr: *mut NativeChimeraDb = db.as_mut_native();
1600    /// let db_ref_1 = ManuallyDrop::new(unsafe { ChimeraDb::from_native(db_ptr) });
1601    /// let db_ref_2 = ManuallyDrop::new(unsafe { ChimeraDb::from_native(db_ptr) });
1602    ///
1603    /// // Both db references are valid and can be used for matching.
1604    /// let mut scratch = ChimeraScratch::blank();
1605    /// scratch.setup_for_db(&db_ref_1)?;
1606    /// scratch.setup_for_db(&db_ref_2)?;
1607    ///
1608    /// let mut matches: Vec<&str> = Vec::new();
1609    /// let e = |_| ChimeraMatchResult::Continue;
1610    /// scratch
1611    ///   .scan_sync(&db_ref_1, "aardvark".into(), |ChimeraMatch { source, .. }| {
1612    ///     matches.push(unsafe { source.as_str() });
1613    ///     ChimeraMatchResult::Continue
1614    ///   }, e)?;
1615    /// scratch
1616    ///   .scan_sync(&db_ref_2, "aardvark".into(), |ChimeraMatch { source, .. }| {
1617    ///     matches.push(unsafe { source.as_str() });
1618    ///     ChimeraMatchResult::Continue
1619    ///   }, e)?;
1620    /// assert_eq!(&matches, &["aa", "a", "aa", "a"]);
1621    /// # Ok(())
1622    /// # }
1623    /// ```
1624    pub const unsafe fn from_native(p: *mut NativeChimeraDb) -> Self { Self(p) }
1625
1626    /// Get a read-only reference to the db allocation.
1627    ///
1628    /// This method is mostly used internally and cast to a pointer to provide
1629    /// to the chimera native library methods.
1630    pub fn as_ref_native(&self) -> &NativeChimeraDb { unsafe { &*self.0 } }
1631
1632    /// Get a mutable reference to the db allocation.
1633    ///
1634    /// The result of this method can be cast to a pointer and provided to
1635    /// [`Self::from_native()`].
1636    pub fn as_mut_native(&mut self) -> &mut NativeChimeraDb { unsafe { &mut *self.0 } }
1637
1638    /// Free the underlying db allocation.
1639    ///
1640    /// # Safety
1641    /// This method must be called at most once over the lifetime of each db
1642    /// allocation. It is called by default on drop, so
1643    /// [`ManuallyDrop`](mem::ManuallyDrop) is recommended to wrap instances
1644    /// that reference external data in order to avoid attempting to free the
1645    /// referenced data.
1646    ///
1647    /// ## Only Frees Memory
1648    /// This method performs no processing other than freeing the allocated
1649    /// memory, so it can be skipped without leaking resources if the
1650    /// underlying [`NativeChimeraDb`] allocation is freed by some other means.
1651    pub unsafe fn try_drop(&mut self) -> Result<(), ChimeraRuntimeError> {
1652      ChimeraRuntimeError::from_native(hs::ch_free_database(self.as_mut_native()))
1653    }
1654  }
1655
1656  impl ops::Drop for ChimeraDb {
1657    fn drop(&mut self) {
1658      unsafe {
1659        self.try_drop().unwrap();
1660      }
1661    }
1662  }
1663
1664  /// Wrapper for allocated string data returned by [`ChimeraDb::info()`].
1665  pub struct ChimeraDbInfo(ChimeraMiscAllocation);
1666
1667  impl ChimeraDbInfo {
1668    const fn without_null(&self) -> impl slice::SliceIndex<[u8], Output=[u8]> {
1669      ..(self.0.len() - 1)
1670    }
1671
1672    /// Return a view of the allocated string data.
1673    ///
1674    /// Chimera will always return valid UTF-8 data for this string, so it skips
1675    /// the validity check. Note that the returned string does not include
1676    /// the trailing null byte allocated by the underlying chimera library.
1677    pub fn as_str(&self) -> &str {
1678      unsafe { str::from_utf8_unchecked(&self.0.as_slice()[self.without_null()]) }
1679    }
1680
1681    /// Write out metadata for `db` into a newly allocated region.
1682    pub fn extract_db_info(db: &ChimeraDb) -> Result<Self, ChimeraRuntimeError> {
1683      let mut info = ptr::null_mut();
1684      ChimeraRuntimeError::from_native(unsafe {
1685        hs::ch_database_info(db.as_ref_native(), &mut info)
1686      })?;
1687      let len = unsafe { CStr::from_ptr(info) }.to_bytes_with_nul().len();
1688      assert!(len > 0);
1689
1690      let ret = ChimeraMiscAllocation {
1691        data: unsafe { mem::transmute(info) },
1692        len,
1693      };
1694
1695      Ok(Self(ret))
1696    }
1697  }
1698
1699  impl fmt::Debug for ChimeraDbInfo {
1700    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1701      write!(f, "ChimeraDbInfo({:?})", self.as_str())
1702    }
1703  }
1704
1705  impl fmt::Display for ChimeraDbInfo {
1706    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_str()) }
1707  }
1708
1709  impl cmp::PartialEq for ChimeraDbInfo {
1710    fn eq(&self, other: &Self) -> bool { self.as_str().eq(other.as_str()) }
1711  }
1712
1713  impl cmp::Eq for ChimeraDbInfo {}
1714
1715  impl cmp::PartialOrd for ChimeraDbInfo {
1716    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { Some(self.cmp(other)) }
1717  }
1718
1719  impl cmp::Ord for ChimeraDbInfo {
1720    fn cmp(&self, other: &Self) -> cmp::Ordering { self.as_str().cmp(other.as_str()) }
1721  }
1722
1723  impl hash::Hash for ChimeraDbInfo {
1724    fn hash<H>(&self, state: &mut H)
1725    where H: hash::Hasher {
1726      self.as_str().hash(state);
1727    }
1728  }
1729}