pub struct Stalloc<const L: usize, const B: usize>where
Align<B>: Alignment,{ /* private fields */ }Expand description
A fast first-fit memory allocator.
When you create an instance of this allocator, you pass in a value for L and B.
L is the number of blocks, and B is the size of each block in bytes. The total size of this type
comes out to L * B + 4 bytes, of which L * B can be used (4 bytes are needed to hold some metadata).
B must be a power of two from 4 and 2^29, and L must be a number in the range 1..65536.
B represents the smallest unit of memory that the allocator can manage. If B == 16, then asking
for 17 bytes will give you a 32 byte allocation (the amount is rounded up).
The alignment of the allocator is always equal to B. For maximum efficiency, it is recommended
to set B equal to the alignment of the type you expect to store the most of. For example, if you’re storing
a lot of u64s, you should set B == 8.
Note that Stalloc cannot be used as a global allocator because it is not thread-safe. To switch out the global
allocator, use SyncStalloc or UnsafeStalloc, which can be used concurrently.
Implementations§
Source§impl<const L: usize, const B: usize> Stalloc<L, B>where
Align<B>: Alignment,
impl<const L: usize, const B: usize> Stalloc<L, B>where
Align<B>: Alignment,
Sourcepub const fn new() -> Self
pub const fn new() -> Self
Initializes a new empty Stalloc instance.
§Examples
use stalloc::Stalloc;
let alloc = Stalloc::<200, 8>::new();Sourcepub const fn is_oom(&self) -> bool
pub const fn is_oom(&self) -> bool
Checks if the allocator is completely out of memory.
If this is false, then you are guaranteed to be able to allocate
a layout with a size and alignment of B bytes.
This runs in O(1).
§Examples
use stalloc::Stalloc;
let alloc = Stalloc::<200, 8>::new();
assert!(!alloc.is_oom());
let ptr = unsafe { alloc.allocate_blocks(200, 1).unwrap() };
assert!(alloc.is_oom());Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Checks if the allocator is empty.
If this is true, then you are guaranteed to be able to allocate
a layout with a size of B * L bytes and an alignment of B bytes.
If this is false, then this is guaranteed to be impossible.
This runs in O(1).
§Examples
use stalloc::Stalloc;
let alloc = Stalloc::<60, 4>::new();
assert!(alloc.is_empty());
let ptr = unsafe { alloc.allocate_blocks(60, 1).unwrap() };
assert!(!alloc.is_empty());
unsafe { alloc.deallocate_blocks(ptr, 60) };
assert!(alloc.is_empty());Sourcepub unsafe fn clear(&self)
pub unsafe fn clear(&self)
§Safety
Calling this function immediately invalidates all pointers into the allocator. Calling
deallocate_blocks() with an invalidated pointer will result in the free list being corrupted.
§Examples
use stalloc::Stalloc;
let alloc = Stalloc::<60, 4>::new();
let ptr1 = unsafe { alloc.allocate_blocks(20, 1) }.unwrap();
let ptr2 = unsafe { alloc.allocate_blocks(20, 1) }.unwrap();
let ptr3 = unsafe { alloc.allocate_blocks(20, 1) }.unwrap();
unsafe { alloc.clear() }; // invalidate all allocated pointers
assert!(alloc.is_empty());Examples found in repository?
9fn main() {
10 let start = Instant::now();
11 for _ in 0..10_000_000 {
12 let mut a = vec![];
13 let mut b = vec![];
14 for i in 0..10 {
15 a.push(i);
16 b.push(i);
17 }
18
19 mem::forget(a);
20 mem::forget(b);
21
22 // By clearing the global allocator, we can quickly drop both vectors together.
23 // SAFETY: There are no more active allocations into `GLOBAL`.
24 unsafe {
25 GLOBAL.clear();
26 }
27 }
28
29 println!("Elapsed: {}ms", start.elapsed().as_millis());
30}Sourcepub unsafe fn allocate_blocks(
&self,
size: usize,
align: usize,
) -> Result<NonNull<u8>, AllocError>
pub unsafe fn allocate_blocks( &self, size: usize, align: usize, ) -> Result<NonNull<u8>, AllocError>
Tries to allocate count blocks. If the allocation succeeds, a pointer is returned. This function
never allocates more than necessary. Note that align is measured in units of B.
§Safety
size must be nonzero, and align must be a power of 2 in the range 1..=2^29 / B.
§Errors
Will return AllocError if the allocation was unsuccessful, in which case this function was a no-op.
§Examples
use stalloc::Stalloc;
const BLOCK_SIZE: usize = 4;
let alloc = Stalloc::<10, BLOCK_SIZE>::new();
let ptr = unsafe { alloc.allocate_blocks(10, 1) }.unwrap();
unsafe { ptr.write_bytes(42, 10 * BLOCK_SIZE) };
assert!(alloc.is_oom());Sourcepub unsafe fn deallocate_blocks(&self, ptr: NonNull<u8>, size: usize)
pub unsafe fn deallocate_blocks(&self, ptr: NonNull<u8>, size: usize)
Deallocates a pointer. This function always succeeds.
§Safety
ptr must point to an allocation, and size must be the number of blocks
in the allocation. That is, size is always in 1..=L.
§Examples
use stalloc::Stalloc;
let alloc = Stalloc::<100, 16>::new();
let ptr = unsafe { alloc.allocate_blocks(100, 1) }.unwrap();
assert!(alloc.is_oom());
unsafe { alloc.deallocate_blocks(ptr, 100) };
assert!(alloc.is_empty());Sourcepub unsafe fn shrink_in_place(
&self,
ptr: NonNull<u8>,
old_size: usize,
new_size: usize,
)
pub unsafe fn shrink_in_place( &self, ptr: NonNull<u8>, old_size: usize, new_size: usize, )
Shrinks the allocation. This function always succeeds and never reallocates.
§Safety
ptr must point to a valid allocation of old_size blocks, and new_size must be in 1..old_size.
§Examples
use stalloc::Stalloc;
let alloc = Stalloc::<100, 16>::new();
let ptr = unsafe { alloc.allocate_blocks(100, 1) }.unwrap();
assert!(alloc.is_oom());
// shrink the allocation from 100 to 90 blocks
unsafe { alloc.shrink_in_place(ptr, 100, 90) };
assert!(!alloc.is_oom());Sourcepub unsafe fn grow_in_place(
&self,
ptr: NonNull<u8>,
old_size: usize,
new_size: usize,
) -> Result<(), AllocError>
pub unsafe fn grow_in_place( &self, ptr: NonNull<u8>, old_size: usize, new_size: usize, ) -> Result<(), AllocError>
Tries to grow the current allocation in-place. If that isn’t possible, this function is a no-op.
§Safety
ptr must point to a valid allocation of old_size blocks. Also, new_size > old_size.
§Errors
Will return AllocError if the grow was unsuccessful, in which case this function was a no-op.
§Examples
use stalloc::Stalloc;
let alloc = Stalloc::<100, 16>::new();
let ptr = unsafe { alloc.allocate_blocks(25, 1) }.unwrap();
assert!(!alloc.is_oom());
// grow the allocation from 25 to 100 blocks
unsafe { alloc.grow_in_place(ptr, 25, 100) }.unwrap();
assert!(alloc.is_oom());Sourcepub unsafe fn grow_up_to(
&self,
ptr: NonNull<u8>,
old_size: usize,
new_size: usize,
) -> usize
pub unsafe fn grow_up_to( &self, ptr: NonNull<u8>, old_size: usize, new_size: usize, ) -> usize
Tries to grow the current allocation in-place. If that isn’t possible, the allocator grows by as much
as it is able to, and the new length of the allocation is returned. The new length is guaranteed to be
in the range old_size..=new_size.
§Safety
ptr must point to a valid allocation of old_size blocks. Also, new_size > old_size.
§Examples
use stalloc::Stalloc;
let alloc1 = Stalloc::<7, 4>::new();
unsafe {
let ptr = alloc1.allocate_blocks(3, 1).unwrap(); // allocate 3 blocks
let new_size = alloc1.grow_up_to(ptr, 3, 9999); // try to grow to a ridiculous amount
assert_eq!(new_size, 7); // can only grow up to 7
}
let alloc2 = Stalloc::<21, 16>::new();
unsafe {
let ptr = alloc2.allocate_blocks(9, 1).unwrap(); // allocate 9 blocks
let new_size = alloc2.grow_up_to(ptr, 9, 21);
assert_eq!(new_size, 21); // grow was successful
}Trait Implementations§
Source§impl<const L: usize, const B: usize> ChainableAlloc for Stalloc<L, B>where
Align<B>: Alignment,
SAFETY: Since zero-sized allocations unconditionally succeed, it can be assumed
that a zero-sized allocation was allocated by this Stalloc. Otherwise, check whether
the pointer is contained within this Stalloc’s buffer.
impl<const L: usize, const B: usize> ChainableAlloc for Stalloc<L, B>where
Align<B>: Alignment,
SAFETY: Since zero-sized allocations unconditionally succeed, it can be assumed that a zero-sized allocation was allocated by this Stalloc. Otherwise, check whether the pointer is contained within this Stalloc’s buffer.