Crate stackalloc[][src]

Expand description

Safe runtime stack allocations

Provides methods for Rust to access and use runtime stack allocated buffers in a safe way. This is accomplished through a helper function that takes a closure of FnOnce that takes the stack allocated buffer slice as a parameter. The slice is considered to be valid only until this closure returns, at which point the stack is reverted back to the caller of the helper function. If you need a buffer that can be moved, use Vec or statically sized arrays. The memory is allocated on the closure’s caller’s stack frame, and is deallocated when the caller returns.

This slice will be properly formed with regards to the expectations safe Rust has on slices. However, it is still possible to cause a stack overflow by allocating too much memory, so use this sparingly and never allocate unchecked amounts of stack memory blindly.

Examples

Allocating a byte buffer on the stack.

fn copy_with_buffer<R: Read, W: Write>(mut from: R, mut to: W, bufsize: usize) -> io::Result<usize>
{
  alloca_zeroed(bufsize, move |buf| -> io::Result<usize> {
   let mut read;
   let mut completed = 0;
   while { read = from.read(&mut buf[..])?; read != 0} {
    to.write_all(&buf[..read])?;
    completed += read;
   }
   Ok(completed)
  })
}

Arbitrary types

Allocating a slice of any type on the stack.

stackalloc(5, "str", |slice: &mut [&str]| {
 assert_eq!(&slice[..], &["str"; 5]);
});

Dropping

The wrapper handles dropping of types that require it.

stackalloc_with(5, || vec![String::from("string"); 10], |slice| {
 assert_eq!(&slice[0][0][..], "string");  
}); // The slice's elements will be dropped here

MaybeUninit

You can get the aligned stack memory directly with no initialisation.

stackalloc_uninit(5, |slice| {
 for s in slice.iter_mut()
 {
   *s = MaybeUninit::new(String::new());
 }
 // SAFETY: We have just initialised all elements of the slice.
 let slice = unsafe { stackalloc::helpers::slice_assume_init_mut(slice) };

 assert_eq!(&slice[..], &vec![String::new(); 5][..]);

 // SAFETY: We have to manually drop the slice in place to ensure its elements are dropped, as `stackalloc_uninit` does not attempt to drop the potentially uninitialised elements.
 unsafe {
   std::ptr::drop_in_place(slice as *mut [String]);
 }
});

Performance

For small (1k or lower) element arrays stackalloc can outperform Vec by about 50% or more. This performance difference decreases are the amount of memory allocated grows.

  • test tests::bench::stackalloc_of_uninit_bytes_known … bench: 3 ns/iter (+/- 0)
  • test tests::bench::stackalloc_of_uninit_bytes_unknown … bench: 3 ns/iter (+/- 0)
  • test tests::bench::stackalloc_of_zeroed_bytes_known … bench: 22 ns/iter (+/- 0)
  • test tests::bench::stackalloc_of_zeroed_bytes_unknown … bench: 17 ns/iter (+/- 0)
  • test tests::bench::vec_of_uninit_bytes_known … bench: 13 ns/iter (+/- 0)
  • test tests::bench::vec_of_uninit_bytes_unknown … bench: 55 ns/iter (+/- 0)
  • test tests::bench::vec_of_zeroed_bytes_known … bench: 36 ns/iter (+/- 2)
  • test tests::bench::vec_of_zeroed_bytes_unknown … bench: 37 ns/iter (+/- 0)

License

MIT licensed

Re-exports

pub use avec::AVec;

Modules

A Vec-like wrapper type that only allocates if a provided buffer is first exhausted.

A module of helper functions for slice memory manipulation

Functions

Allocate a runtime length uninitialised byte buffer on the stack, call callback with this buffer, and then deallocate the buffer.

Allocate a runtime length zeroed byte buffer on the stack, call callback with this buffer, and then deallocate the buffer.

Allocate a runtime length slice of T on the stack, fill it by cloning init, call callback with this buffer, and then drop and deallocate the buffer.

Collect an exact size iterator into a stack allocated slice, call callback with this buffer, and then drop and deallocate the buffer.

Collect an iterator into a stack allocated buffer, call callback with this buffer, and then drop and deallocate the buffer.

Allocate a runtime length slice of uninitialised T on the stack, call callback with this buffer, and then deallocate the buffer.

Allocate a runtime length slice of T on the stack, fill it by calling init_with, call callback with this buffer, and then drop and deallocate the buffer.

Allocate a runtime length slice of T on the stack, fill it by calling T::default(), call callback with this buffer, and then drop and deallocate the buffer.

Collect an iterator into a stack allocated buffer up to size elements, call callback with this buffer, and then drop and deallocate the buffer.