Struct Scratch

Source
pub struct Scratch(/* private fields */);
Expand description

Mutable workspace required by all search methods.

This type also serves as the most general entry point to the various synchronous and asynchronous string search methods.

Implementations§

Source§

impl Scratch

§Setup Methods

These methods create a new scratch space or initialize it against a database. Database::allocate_scratch() is also provided as a convenience method to combine the creation and initialization steps.

Source

pub const fn blank() -> Self

Return an uninitialized scratch space without allocation.

Source

pub fn setup_for_db( &mut self, db: &Database, ) -> Result<(), VectorscanRuntimeError>

Initialize this scratch space against the given db.

A single scratch space can be initialized against multiple databases, but exclusive mutable access is required to perform a search, so Self::try_clone() can be used to obtain multiple copies of a multiply-initialized scratch space.

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*, matchers::*, state::*, sources::*};

   let a_expr: Expression = "a+".parse()?;
   let a_db = a_expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?;

   let b_expr: Expression = "b+".parse()?;
   let b_db = b_expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?;

   let mut scratch = Scratch::blank();
   scratch.setup_for_db(&a_db)?;
   scratch.setup_for_db(&b_db)?;

   let s: ByteSlice = "ababaabb".into();

   let mut matches: Vec<&str> = Vec::new();
   scratch
     .scan_sync(&a_db, s, |m| {
       matches.push(unsafe { m.source.as_str() });
       MatchResult::Continue
     })?;
   assert_eq!(&matches, &["a", "a", "a", "aa"]);

   matches.clear();
   scratch
     .scan_sync(&b_db, s, |m| {
       matches.push(unsafe { m.source.as_str() });
       MatchResult::Continue
     })?;
   assert_eq!(&matches, &["b", "b", "b", "bb"]);
   Ok(())
 }
Source§

impl Scratch

§Synchronous String Scanning

Vectorscan’s string search interface requires a C function pointer to call synchronously upon each match. This guarantee of synchronous invocation enables the function to mutate data under the expectation of exclusive access (we formalize this guarantee as FnMut). While Rust closures cannot be converted into C function pointers automatically, vectorscan also passes in a *mut c_void context pointer to each invocation of the match callback, and this can be used to hold a type-erased container for a Rust-level closure.

§Ephemeral Match Objects

In all of these synchronous search methods, the provided match callback f is converted into a dyn reference and invoked within the C function pointer provided to the vectorscan library. Match objects like Match provided to the match callback are synthesized by this crate and are not preserved after each invocation of f, so the match callback must modify some external state to store match results.

Source

pub fn scan_sync<'data>( &mut self, db: &Database, data: ByteSlice<'data>, f: impl FnMut(Match<'data>) -> MatchResult, ) -> Result<(), VectorscanRuntimeError>

Synchronously scan a single contiguous string.

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*, matchers::*, error::*};

   let a_expr: Expression = "a+".parse()?;
   let b_expr: Expression = "b+".parse()?;
   let flags = Flags::SOM_LEFTMOST;
   let expr_set = ExpressionSet::from_exprs([&a_expr, &b_expr])
     .with_flags([flags, flags])
     .with_ids([ExprId(1), ExprId(2)]);
   let db = expr_set.compile(Mode::BLOCK)?;
   let mut scratch = db.allocate_scratch()?;

   let mut matches: Vec<&str> = Vec::new();
   {
     let mut f = |Match { source, .. }| {
       matches.push(unsafe { source.as_str() });
       MatchResult::Continue
     };
     scratch.scan_sync(&db, "aardvark".into(), &mut f)?;
     scratch.scan_sync(&db, "imbibbe".into(), &mut f)?;
   }
   assert_eq!(&matches, &["a", "aa", "a", "b", "b", "bb"]);

   let ret = scratch.scan_sync(&db, "abwuebiaubeb".into(), |_| MatchResult::CeaseMatching);
   assert!(matches![ret, Err(VectorscanRuntimeError::ScanTerminated)]);
   Ok(())
 }
Source

pub fn scan_sync_vectored<'data>( &mut self, db: &Database, data: VectoredByteSlices<'data, 'data>, f: impl FnMut(VectoredMatch<'data>) -> MatchResult, ) -> Result<(), VectorscanRuntimeError>

Available on crate feature vectored only.

Synchronously scan a slice of vectored string data.

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*, sources::*, matchers::*};

   let a_plus: Expression = "a+".parse()?;
   let b_plus: Expression = "b+".parse()?;
   let asdf: Expression = "asdf(.)".parse()?;
   let flags = Flags::SOM_LEFTMOST;
   let expr_set = ExpressionSet::from_exprs([&a_plus, &b_plus, &asdf])
     .with_flags([flags, flags, flags])
     .with_ids([ExprId(0), ExprId(3), ExprId(2)]);
   let db = expr_set.compile(Mode::VECTORED)?;
   let mut scratch = db.allocate_scratch()?;

   let data: [ByteSlice; 4] = [
     "aardvark".into(),
     "imbibbe".into(),
     "leas".into(),
     "dfeg".into(),
   ];
   let mut matches: Vec<(u32, String)> = Vec::new();
   scratch
     .scan_sync_vectored(
       &db,
       data.as_ref().into(),
       |VectoredMatch { id: ExpressionIndex(id), source, .. }| {
         let joined = source.iter_slices()
           .map(|s| unsafe { s.as_str() })
           .collect::<Vec<_>>()
           .concat();
         matches.push((id, joined));
         MatchResult::Continue
     })?;
   assert_eq!(matches, vec![
     (0, "a".to_string()),
     (0, "aa".to_string()),
     (0, "a".to_string()),
     (3, "b".to_string()),
     (3, "b".to_string()),
     (3, "bb".to_string()),
     (0, "a".to_string()),
     // NB: This match result crosses a slice boundary!
     (2, "asdfe".to_string()),
   ]);
   Ok(())
 }

Note that if you do not need to know anything other than the offsets of the matches, then you could also try using Self::scan_sync_vectored_stream(), which passes StreamMatch to the callback method (although a subsequent Scratch::flush_eod_sync() call is necessary to match against end-of-stream markers; see MatchAtEndBehavior, which is returned by Expression::info()).

Source

pub fn scan_sync_stream<'data, 'code>( &mut self, live: &mut LiveStream, matcher: &mut StreamMatcher<'code>, data: ByteSlice<'data>, ) -> Result<(), VectorscanRuntimeError>

Available on crate feature stream only.

Write data into the stream object live.

This method is mostly used internally; consumers of this crate will likely prefer to call ScratchStreamSink::scan().

Source

pub fn scan_sync_vectored_stream<'data, 'code>( &mut self, live: &mut LiveStream, matcher: &mut StreamMatcher<'code>, data: VectoredByteSlices<'data, 'data>, ) -> Result<(), VectorscanRuntimeError>

Available on crate features stream and vectored only.

Write vectored data into the stream object live.

This method is mostly used internally; consumers of this crate will likely prefer to call ScratchStreamSink::scan_vectored().

Source

pub fn flush_eod_sync<'code>( &mut self, live: &mut LiveStream, matcher: &mut StreamMatcher<'code>, ) -> Result<(), VectorscanRuntimeError>

Available on crate feature stream only.

Process any EOD (end-of-data) matches for the stream object live.

This method is mostly used internally; consumers of this crate will likely prefer to call ScratchStreamSink::flush_eod().

Source§

impl Scratch

§Convenience Methods

These methods provide quick access to common regex use cases without needing to provide a closure.

Source

pub fn first_match<'data>( &mut self, db: &Database, data: ByteSlice<'data>, ) -> Result<Option<Match<'data>>, VectorscanRuntimeError>

Return the result from the first match callback, or None if no match was found.

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*};

   let expr: Expression = "a+".parse()?;
   let db = expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?;
   let mut scratch = db.allocate_scratch()?;

   let msg = "aardvark";
   let first_a = scratch.first_match(&db, msg.into())?.unwrap().source.as_slice();
   assert_eq!(first_a, b"a");
   assert_eq!(first_a.as_ptr(), msg.as_bytes().as_ptr());
   Ok(())
 }
Source

pub fn full_match<'data>( &mut self, db: &Database, data: ByteSlice<'data>, ) -> Result<Option<Match<'data>>, VectorscanRuntimeError>

Return the first match result that matches the full length of the input string, or None if no match could be found.

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*};

   let expr: Expression = "a+sdf".parse()?;
   let db = expr.compile(Flags::default(), Mode::BLOCK)?;
   let mut scratch = db.allocate_scratch()?;

   let msg = "asdf";
   let m = scratch.full_match(&db, msg.into())?.unwrap().source.as_slice();
   assert_eq!(m, msg.as_bytes());
   assert_eq!(m.as_ptr(), msg.as_bytes().as_ptr());
   Ok(())
 }
Source§

impl Scratch

§Asynchronous String Scanning

While the synchronous search methods can be used from async or multi-threaded code, a multithreaded execution environment offers particular opportunities to improve search latency and/or throughput. These methods are written to expose an idiomatic Rust interface for highly parallel searching.

§Minimizing Work in the Match Callback

Because the vectorscan match callback is always invoked synchronously, it also stops the regex engine from making any further progress while it executes. If the match callback does too much work before returning control to the vectorscan library, this may harm search performance by thrashing the processor caches or other state.[FIXME: citation needed/benchmark this]

§Producer-Consumer Pattern

Therefore, one useful pattern is to write the match object to a queue and quickly exit the match callback, then read matches from the queue in another thread of control in order to decouple match processing from text searching. Multi-processor systems in particular may be able to achieve higher search throughput if a separate thread is used to perform further match processing in parallel while a vectorscan search is executing.

Note that the Synchronous String Scanning API can still be used to implement producer-consumer match queues! In fact, Self::scan_channel() is implemented just by writing to a queue within the match callback provided to an internal Self::scan_sync() call! However, async streams provide a natural interface to wrap the output of a queue, so the methods in this section return a Stream, which can be consumed or extended by external code such as futures_util::TryStreamExt.

§Match Objects with Lifetimes

The match callbacks for these methods accept a reference to the match object, instead of owning the match result like the Ephemeral Match Objects from synchronous search methods. Even though most match objects are Copy anyway, this reference interface is used to clarify that the callback should only determine whether to continue matching, while the underlying match object will be written into the returned stream and should be retrieved from there instead for further processing.

Source

pub fn scan_channel<'data>( &mut self, db: &Database, data: ByteSlice<'data>, f: impl FnMut(&Match<'data>) -> MatchResult + Send, ) -> impl Stream<Item = Result<Match<'data>, ScanError>>

Available on crate feature async only.

Asynchronously scan a single contiguous string.

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> { tokio_test::block_on(async {
   use vectorscan::{expression::*, flags::*, matchers::*, error::*};
   use futures_util::TryStreamExt;

   let a_expr: Expression = "a+".parse()?;
   let b_expr: Expression = "b+".parse()?;
   let flags = Flags::SOM_LEFTMOST;
   let expr_set = ExpressionSet::from_exprs([&a_expr, &b_expr])
     .with_flags([flags, flags])
     .with_ids([ExprId(1), ExprId(2)]);
   let db = expr_set.compile(Mode::BLOCK)?;
   let mut scratch = db.allocate_scratch()?;

   let matches: Vec<&str> = scratch
     .scan_channel(&db, "aardvark".into(), |_| MatchResult::Continue)
     .map_ok(|Match { source, .. }| unsafe { source.as_str() })
     .try_collect()
     .await?;
   assert_eq!(&matches, &["a", "aa", "a"]);

   let matches: Vec<&str> = scratch
     .scan_channel(&db, "imbibbe".into(), |_| MatchResult::Continue)
     .map_ok(|Match { source, .. }| unsafe { source.as_str() })
     .try_collect()
     .await?;
   assert_eq!(&matches, &["b", "b", "bb"]);

   let ret = scratch.scan_channel(
     &db,
     "abwuebiaubeb".into(),
     |_| MatchResult::CeaseMatching,
   ).try_for_each(|_| async { Ok(()) })
    .await;
   assert!(matches![ret, Err(ScanError::ReturnValue(VectorscanRuntimeError::ScanTerminated))]);
   Ok(())
 })}
Source

pub fn scan_channel_vectored<'data>( &mut self, db: &Database, data: VectoredByteSlices<'data, 'data>, f: impl FnMut(&VectoredMatch<'data>) -> MatchResult + Send, ) -> impl Stream<Item = Result<VectoredMatch<'data>, ScanError>>

Available on crate features async and vectored only.

Asynchronously scan a slice of vectored string data.

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> { tokio_test::block_on(async {
   use vectorscan::{expression::*, flags::*, sources::*, matchers::*};
   use futures_util::TryStreamExt;

   let a_plus: Expression = "a+".parse()?;
   let b_plus: Expression = "b+".parse()?;
   let asdf: Expression = "asdf(.)".parse()?;
   let flags = Flags::SOM_LEFTMOST;
   let expr_set = ExpressionSet::from_exprs([&a_plus, &b_plus, &asdf])
     .with_flags([flags, flags, flags])
     .with_ids([ExprId(0), ExprId(3), ExprId(2)]);
   let db = expr_set.compile(Mode::VECTORED)?;
   let mut scratch = db.allocate_scratch()?;

   let data: [ByteSlice; 4] = [
     "aardvark".into(),
     "imbibbe".into(),
     "leas".into(),
     "dfeg".into(),
   ];
   let matches: Vec<(u32, String)> = scratch
     .scan_channel_vectored(&db, data.as_ref().into(), |_| MatchResult::Continue)
     .map_ok(|VectoredMatch { id: ExpressionIndex(id), source, .. }| {
       let joined = source.iter_slices()
         .map(|s| unsafe { s.as_str() })
         .collect::<Vec<_>>()
         .concat();
       (id, joined)
     })
     .try_collect()
     .await?;
   assert_eq!(matches, vec![
     (0, "a".to_string()),
     (0, "aa".to_string()),
     (0, "a".to_string()),
     (3, "b".to_string()),
     (3, "b".to_string()),
     (3, "bb".to_string()),
     (0, "a".to_string()),
     // NB: This match result crosses a slice boundary!
     (2, "asdfe".to_string()),
   ]);
   Ok(())
 })}
Source§

impl Scratch

§Managing Allocations

These methods provide access to the underlying memory allocation containing the data for the scratch space. They can be used to control the memory location used for the scratch space, or to preserve scratch allocations across weird lifetime constraints.

Note that Self::scratch_size() can be used to determine the size of the memory allocation pointed to by Self::as_ref_native() and Self::as_mut_native().

Source

pub unsafe fn from_native(p: *mut NativeScratch) -> Self

Wrap the provided allocation p.

§Safety

The pointer p must be null or have been produced by Self::as_mut_native().

This method also makes it especially easy to create multiple references to the same allocation, which will then cause a double free when Self::try_drop() is called more than once for the same scratch allocation. To avoid this, wrap the result in a ManuallyDrop:

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*, matchers::*, state::*};
   use std::{mem::ManuallyDrop, ptr};

   // Compile a legitimate db:
   let expr: Expression = "a+".parse()?;
   let db = expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?;
   let mut scratch = db.allocate_scratch()?;

   // Create two new references to that allocation,
   // wrapped to avoid calling the drop code:
   let scratch_ptr: *mut NativeScratch = scratch
     .as_mut_native()
     .map(|p| p as *mut NativeScratch)
     .unwrap_or(ptr::null_mut());
   let mut scratch_ref_1 = ManuallyDrop::new(unsafe { Scratch::from_native(scratch_ptr) });
   let mut scratch_ref_2 = ManuallyDrop::new(unsafe { Scratch::from_native(scratch_ptr) });

   // Both scratch references are valid and can be used for matching.
   let mut matches: Vec<&str> = Vec::new();
   scratch_ref_1
     .scan_sync(&db, "aardvark".into(), |Match { source, .. }| {
       matches.push(unsafe { source.as_str() });
       MatchResult::Continue
     })?;
   scratch_ref_2
     .scan_sync(&db, "aardvark".into(), |Match { source, .. }| {
       matches.push(unsafe { source.as_str() });
       MatchResult::Continue
     })?;
   assert_eq!(&matches, &["a", "aa", "a", "a", "aa", "a"]);
   Ok(())
 }
Source

pub fn as_ref_native(&self) -> Option<&NativeScratch>

Get a read-only reference to the scratch allocation.

This method is mostly used internally and converted to a nullable pointer to provide to the vectorscan native library methods.

Source

pub fn as_mut_native(&mut self) -> Option<&mut NativeScratch>

Get a mutable reference to the scratch allocation.

The result of this method can be converted to a nullable pointer and provided to Self::from_native().

Source

pub fn scratch_size(&self) -> Result<usize, VectorscanRuntimeError>

Return the size of the scratch allocation.

Using Flags::UCP explodes the size of character classes, which increases the size of the scratch space:

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*};

   let expr: Expression = r"\w".parse()?;
   let utf8_db = expr.compile(Flags::UTF8 | Flags::UCP, Mode::BLOCK)?;
   let ascii_db = expr.compile(Flags::default(), Mode::BLOCK)?;

   let utf8_scratch = utf8_db.allocate_scratch()?;
   let ascii_scratch = ascii_db.allocate_scratch()?;

   // Including UTF-8 classes increases the size:
   assert!(utf8_scratch.scratch_size()? > ascii_scratch.scratch_size()?);
   Ok(())
 }

This size corresponds to the requested allocation size passed to the scratch allocator:

 #[cfg(all(feature = "alloc", feature = "compiler"))]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*, state::*, alloc::*};
   use std::alloc::System;

   // Wrap the standard Rust System allocator.
   let tracker = LayoutTracker::new(System.into());
   // Register it as the allocator for databases.
   assert!(set_scratch_allocator(tracker)?.is_none());

   let expr: Expression = r"\w".parse()?;
   let utf8_db = expr.compile(Flags::UTF8 | Flags::UCP, Mode::BLOCK)?;
   let mut utf8_scratch = utf8_db.allocate_scratch()?;

   // Get the scratch allocator we just registered and view its live allocations:
   let allocs = get_scratch_allocator().as_ref().unwrap().current_allocations();
   // Verify that only the single known scratch was allocated:
   assert_eq!(1, allocs.len());
   let (p, layout) = allocs[0];
   // The allocation was made within 0x30 bytes to the left of the returned scratch pointer.
   let alloc_ptr = utf8_scratch.as_mut_native().unwrap() as *mut NativeScratch as *mut u8;
   let p_diff = (alloc_ptr as usize) - (p.as_ptr() as usize);
   assert!(p_diff <= 0x30);

   // Verify that the allocation size is the same as reported:
   assert_eq!(layout.size(), utf8_scratch.scratch_size()?);
   Ok(())
 }

Every single database of the same mode allocates the exact same scratch size:

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*, state::*};

   let a_expr: Expression = "a+".parse()?;
   let b_expr: Expression = "b+".parse()?;
   let a_db = a_expr.compile(Flags::default(), Mode::BLOCK)?;
   let b_db = b_expr.compile(Flags::default(), Mode::BLOCK)?;

   let a_scratch = a_db.allocate_scratch()?;
   let b_scratch = b_db.allocate_scratch()?;
   let mut abc_scratch = Scratch::blank();
   abc_scratch.setup_for_db(&a_db)?;
   abc_scratch.setup_for_db(&b_db)?;

   assert_eq!(a_scratch.scratch_size()?, abc_scratch.scratch_size()?);
   assert_eq!(a_scratch.scratch_size()?, b_scratch.scratch_size()?);
   assert_eq!(b_scratch.scratch_size()?, abc_scratch.scratch_size()?);

   let c_expr: Expression = "c{,2}|d?e+f?".parse()?;
   let c_db = c_expr.compile(Flags::default(), Mode::BLOCK)?;
   let c_scratch = c_db.allocate_scratch()?;

   assert_eq!(a_scratch.scratch_size()?, c_scratch.scratch_size()?);
   abc_scratch.setup_for_db(&c_db)?;
   assert_eq!(a_scratch.scratch_size()?, abc_scratch.scratch_size()?);
   Ok(())
 }

However, databases of different modes allocate different scratch sizes:

 #[cfg(all(feature = "compiler", feature = "vectored", feature = "stream"))]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*};

   let expr: Expression = "a+".parse()?;
   let block_db = expr.compile(Flags::default(), Mode::BLOCK)?;
   let vectored_db = expr.compile(Flags::default(), Mode::VECTORED)?;
   let stream_db = expr.compile(Flags::default(), Mode::STREAM)?;

   let block_scratch = block_db.allocate_scratch()?;
   let vectored_scratch = vectored_db.allocate_scratch()?;
   let stream_scratch = stream_db.allocate_scratch()?;
   assert!(block_scratch.scratch_size()? > stream_scratch.scratch_size()?);
   assert!(block_scratch.scratch_size()? < vectored_scratch.scratch_size()?);
   Ok(())
 }

Scratch sizes do not change when used:

 #[cfg(feature = "compiler")]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*, matchers::*};

   let expr: Expression = "a+".parse()?;
   let db = expr.compile(Flags::SOM_LEFTMOST, Mode::BLOCK)?;

   let mut scratch = db.allocate_scratch()?;
   let size = scratch.scratch_size()?;

   let mut matches: Vec<&str> = Vec::new();
   scratch
     .scan_sync(&db, "aardvark".into(), |m| {
       matches.push(unsafe { m.source.as_str() });
       MatchResult::Continue
     })?;

   assert_eq!(&matches, &["a", "aa", "a"]);
   assert_eq!(size, scratch.scratch_size()?);
   Ok(())
 }
Source

pub fn try_clone(&self) -> Result<Self, VectorscanRuntimeError>

Generate a new scratch space which can be applied to the same databases as the original.

This new scratch space is allocated in a new region of memory provided by the scratch allocator. This is used to implement Clone.

 #[cfg(all(feature = "alloc", feature = "compiler"))]
 fn main() -> Result<(), vectorscan::error::VectorscanError> {
   use vectorscan::{expression::*, flags::*, alloc::*};
   use std::alloc::System;

   // Wrap the standard Rust System allocator.
   let tracker = LayoutTracker::new(System.into());
   // Register it as the allocator for databases.
   assert!(set_scratch_allocator(tracker)?.is_none());

   let expr: Expression = r"\w".parse()?;
   let utf8_db = expr.compile(Flags::UTF8 | Flags::UCP, Mode::BLOCK)?;
   let scratch1 = utf8_db.allocate_scratch()?;
   let _scratch2 = scratch1.try_clone()?;

   // Get the scratch allocator we just registered and view its live allocations:
   let allocs = get_scratch_allocator().as_ref().unwrap().current_allocations();
   // Verify that only two scratches were allocated:
   assert_eq!(2, allocs.len());
   let (p1, l1) = allocs[0];
   let (p2, l2) = allocs[1];
   assert!(p1 != p2);
   assert!(l1 == l2);
   Ok(())
 }
Source

pub unsafe fn try_drop(&mut self) -> Result<(), VectorscanRuntimeError>

Free the underlying scratch allocation.

§Safety

This method must be called at most once over the lifetime of each scratch allocation. It is called by default on drop, so ManuallyDrop is recommended to wrap instances that reference external data in order to avoid attempting to free the referenced data.

Because the pointer returned by Self::as_mut_native() does not correspond to the entire scratch allocation, this method must be executed in order to avoid leaking resources associated with a scratch space. The memory must not be deallocated elsewhere.

Trait Implementations§

Source§

impl Clone for Scratch

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Scratch

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for Scratch

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl Resource for Scratch

Source§

type Error = VectorscanRuntimeError

Error type for allocation and deallocation. Read more
Source§

fn deep_clone(&self) -> Result<Self, Self::Error>
where Self: Sized,

Request a new memory allocation, then initialize the allocation with the same state as self.
Source§

fn deep_boxed_clone( &self, ) -> Result<Box<dyn Resource<Error = Self::Error>>, Self::Error>

Similar to Self::deep_clone(), but places the concrete type in a heap-allocated box. Read more
Source§

unsafe fn sync_drop(&mut self) -> Result<(), Self::Error>

Deallocate the memory and release any other resources that were owned by this specific object. Read more
Source§

impl Send for Scratch

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<R> Handle for R
where R: Resource,

Source§

type R = R

The underlying fallible resource type that we want to hand out copies of. Read more
Source§

fn wrap(r: <R as Handle>::R) -> R

Given an unboxed resource, produce a handle that has exclusive access to the resource.
Source§

fn clone_handle(&self) -> Result<R, <<R as Handle>::R as Resource>::Error>

Given an unboxed handle, create another instance with a strong reference to it.
Source§

fn boxed_clone_handle( &self, ) -> Result<Box<dyn Handle<R = <R as Handle>::R>>, <<R as Handle>::R as Resource>::Error>

Similar to Self::clone_handle(), but places the concrete type in a heap-allocated box.
Source§

fn handle_ref(&self) -> &<R as Handle>::R

Get a read-only reference to the underlying resource.
Source§

fn get_mut(&mut self) -> Option<&mut <R as Handle>::R>

Return Some if this handle has exclusive access, else None.
Source§

fn ensure_exclusive( &mut self, ) -> Result<(), <<R as Handle>::R as Resource>::Error>

If there are any other strong or weak references to the same resource, disassociate them and clone the resource if necessary.
Source§

fn eq_ref(&self, other: &(dyn Handle<R = Self::R> + 'static)) -> bool

Return whether these two handles point to the same underlying resource allocation.
Source§

fn make_mut(&mut self) -> Result<&mut Self::R, <Self::R as Resource>::Error>

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.