Skip to main content

Talc

Struct Talc 

Source
pub struct Talc<S: Source, B: Binning> {
    pub source: S,
    /* private fields */
}
Expand description

The core allocator type.

To use Talc across multiple threads, e.g. as a global allocator, use TalcLock.

To use Talc in a single thread, e.g. via the Allocator API, use TalcCell.

Talc itself does not exhibit interior mutability. You need a mutable reference to allocate using Talc, therefore it doesn’t implement Allocator or GlobalAlloc itself.

§Generic Parameters

An overview of what to consider:

  • The source contains callbacks for acquiring and reclaiming memory. Implementations provided out of the box include:

  • The binning implementation determines the internal types and operations Talc uses to classify chunks into free-lists and keeps track of free-list occupancy. The default implementation is DefaultBinning. The main reason to deviate from this would be knowing the profile of your allocations well, and being able to divvy them up better and/or faster than the generic algorithm. See Binning and the docs on the trait members for more information.

See the Source and Binning trait documentation for more info.

Fields§

§source: S

The memory source state.

This is user-accessible and can be mutated.

Talc just holds it and calls Source::acquire and Source::resize as documented. Talc doesn’t read/write to it after initialization.

Implementations§

Source§

impl<S: Source, B: Binning> Talc<S, B>

Source

pub fn counters(&self) -> &Counters

Available on crate feature counters only.

Obtain a reference to the internal allocation statistics.

Avoid holding onto the reference as this will block allocations (as you’re effectively holding the lock on the allocator, or preventing a mutable reference being created to the allocator). Reading immediately or cloning the struct is recommended.

Source§

impl<S: Source, B: Binning> Talc<S, B>

Source

pub unsafe fn try_allocate(&mut self, layout: Layout) -> Option<NonNull<u8>>

Allocate a contiguous region of memory according to layout, if possible.

§Safety

layout.size() must be nonzero.

Source

pub unsafe fn allocate(&mut self, layout: Layout) -> Option<NonNull<u8>>

Allocate a contiguous region of memory according to layout, if possible.

§Safety

layout.size() must be nonzero.

Source

pub unsafe fn deallocate(&mut self, ptr: *mut u8, layout: Layout)

Free an allocation.

§Safety

ptr must have been previously allocated given layout.

Source

pub unsafe fn try_grow_in_place( &mut self, ptr: *mut u8, layout: Layout, new_size: usize, ) -> bool

Attempt to grow a previously allocated/reallocated region of memory to new_size.

The return value indicates whether the operation was successful. The validity of the pointer is maintained regardless, but the allocation size does not change if false is returned.

§Safety

ptr must have been previously allocated or reallocated given layout. new_size must be larger or equal to layout.size().

Source

pub unsafe fn shrink(&mut self, ptr: *mut u8, layout: Layout, new_size: usize)

Shrink an allocation to new_size.

This function is infallible given valid inputs, and the reallocation will always be done in-place, maintaining the validity of the pointer.

§Safety
  • ptr must have been previously allocated or reallocated given layout.
  • new_size must be smaller or equal to layout.size().
  • new_size must be nonzero.
Source

pub unsafe fn try_realloc_in_place( &mut self, ptr: *mut u8, layout: Layout, new_size: usize, ) -> bool

Attempt to change the size of an allocation without copying memory.

The return value indicates whether the operation was successful.

This just calls shrink or try_grow_in_place depending on whether new_size is larger or smaller.

If new_size <= layout.size(), then this will always succeed.

§Safety
  • ptr must have been previously allocated or reallocated given layout.
  • new_size must be nonzero.
Source

pub const fn new(source: S) -> Self

Create a new Talc. See Talc’s documentation for more info on it.

You won’t typically want to use Talc directly. Consider:

  • The cell-like TalcCell, for single-threaded allocation. Intended for use with the Allocator API.
  • The lock-based synchronized TalcLock, for multi-threaded allocation. Intended for use as a global allocator.

TalcSyncCell is also available, if required.

As allocators are called with shared references, iterior mutability is required, but Talc doesn’t force any particular form of interior mutability to be used so different wrappers provide different trade-offs. If the the wrapper types above don’t quite fit your use-case, making your own may be best.

Source

pub fn is_metadata_established(&self) -> bool

Indicates whether self has already established its allocator metadata into a heap.

§When is this the case?

A successful call to Talc::claim has not been made.

§What does this imply?

If metadata has not been established, the Talc::claim requires a larger arena to succeed. See min_first_heap_size and min_first_heap_layout.

If metadata has been extablished, then the heap size requirement is much lower.

See Talc::claim for more details.

§How should I use this?

It’s most useful to ensure enough memory is being claimed in Source implementations. If you’re not implementing Source, either the Source implementation you’re using will take care of it for you, or you’ll be claiming memory manually and will know when to consider the extra requirement. Use min_first_heap_size or min_first_heap_layout.

§Why is this mechanism the way it is
  • Talc, like most allocators, requires a block of metadata to track available memory.
  • Talc thus needs to have enough space in the first claimed memory region to put the metadata.
  • This block of metadata is referenced by pointers Talc uses for bookkeeping, and thus cannot be moved.
Source

pub unsafe fn claim( &mut self, base: *mut u8, size: usize, ) -> Option<NonNull<u8>>

Establish a new heap to allocate into.

This does not “combine” with neighboring heaps. Use Talc::extend to achieve this.

Due to alignment requirements, the resulting heap may be slightly smaller than the provided memory on either side.

§Failure modes

The first heap needs to hold Talc’s allocation metadata, this has a fixed size that depends on the Binning configuration. Currently, it’s a little over BIN_COUNT * PTR_SIZE but this is subject to change.

Use min_first_heap_layout or min_first_heap_size to guarantee a successful first claim.

Once the first heap is established, the allocation metadata permanently reserves the start of that heap and all subsequent claims are subject to a much less stringent requirement: None is returned only if size is too small to tag the base and have enough left over to fit a chunk.

§Safety

The region of memory described by base and size must not be mutated externally up until the memory is released with Talc::truncate or Talc::resize or the allocator is no longer active.

  • This rule does not apply to memory that is allocated by self. That’s the caller’s memory until deallocated.
  • This rule does not apply to memory after the returned pointer, that’s unclaimed.

The Source must not forbid manual heap management, otherwise this can cause UB. (The Source implementation will clearly state this in its documentation.)

§Example
static mut ARENA: [u8; 5000] = [0; 5000];

let talc = TalcCell::new(Manual);
let arena = unsafe { talc.claim((&raw mut ARENA).cast(), 5000).unwrap() };
Source

pub unsafe fn reserved(&self, heap_end: NonNull<u8>) -> Reserved

Obtain information about the reserved region of a heap.

Memory in the arena is reserved if there is allocated memory above/within it. The reserved part of a heap cannot be released using Talc::truncate or Talc::resize.

---------------- Linear Memory ----------------

    ├──Heap───────────────────────────────────┤
────┬─────┬───────────┬─────┬───────────┬─────┬────
... | Gap | Allocated | Gap | Allocated | Gap | ...
────┴─────┴───────────┴─────┴───────────┴─────┴────
    ├──Reserved─────────────────────────┤

§Return Value

See Reserved. In short, this function indicates where the top of the reserved portion of the heap is, and whether any of the heap is reserved. (If none of the heap is reserved, the “top of the reserved portion” is the bottom of the heap.)

Talc::truncate and Talc::resize will not release bytes below the top of the reserved region. (You can pass null into these functions and they’ll truncate down to the reserved region, but no further.)

§Atomicity

Be aware that the reserved region may change before you use the info if you don’t own the allocator or hold a lock on it.

However, you can use Talc::truncate and Talc::resize correctly without consulting this value at all, as they respect the reserved region automatically.

§Safety
  • heap_end must have been previously acquired from this instance of Talc and has not since changed. (e.g. resizing the heap and then using an old heap_end pointer is an error.)
Source

pub unsafe fn extend( &mut self, heap_end: NonNull<u8>, new_end: *mut u8, ) -> NonNull<u8>

Extend the heap’s end from heap_end to new_end.

Due to alignment requirements, the resulting pointer indicating the top of the heap may not quite reach new_end. The difference will be less than CHUNK_UNIT.

If new_end - heap_end isn’t large enough (less than a CHUNK_UNIT), this call does nothing, returning heap_end.

§Safety
  • arena must be managed by this instance of the allocator.
  • The memory in heap_end..new_end must be exclusively writeable by this instance of the allocator for the lifetime arena unless truncated away or the allocator is no longer active.
    • Note that any memory above the returned pointer is unclaimed by the allocator and not subject to this requirement.
    • Note that any memory in the heap that is allocated by self later on is also not subject to this requirement for the duration of the allocation’s lifetime (this is your memory that you allocated; use it).
  • The Source must not forbid manual heap management, otherwise this can cause UB. (The Source implementation will clearly state this in its documentation.)
§Example
static mut ARENA: [u8; 5000] = [0; 5000];
use talc::{*, source::*};
let talc = TalcCell::new(Manual);

let mut heap_end = unsafe { talc.claim((&raw mut ARENA).cast(), 2500).unwrap() };
unsafe { talc.extend(heap_end, ARENA.as_mut_ptr_range().end) };
Source

pub unsafe fn truncate( &mut self, heap_end: NonNull<u8>, new_end: *mut u8, ) -> Option<NonNull<u8>>

Reduce the heap’s extent from heap_end to new_end.

Returns the new heap end, or otherwise None if the heap would be empty or too small to allocate into (less than a CHUNK_UNIT), and is thus deleted.

If new_end is greater or equal to heap_end, this does nothing and returns heap_end.

The extent cannot be reduced further than what is indicated by Talc::reserved. Attempting to do so (e.g. setting new_end to null_mut) will truncate as much as valid (i.e. down to the reserved region).

Due to alignment requirements, the resulting heap end might be slightly lower than requested by a difference of less than CHUNK_UNIT.

All memory between the resulting pointer and heap_end, if any, is released back to the caller. You no longer need to guarantee that unallocated memory in this region is not mutated. (This is relevant to the safety contract of Talc::claim and Talc::extend.)

§Safety
  • The heap must be managed by this instance of the allocator.
  • heap_end must have been previously returned as an arena end by this allocator, and not subsequently modified. i.e. it must be the up-to-date arena end.
  • The Source must not forbid manual heap management, otherwise this can cause UB. (The Source implementation will clearly state this in its documentation.)
§Example
static mut ARENA: [u8; 5000] = [0; 5000];

let mut talc = TalcCell::new(Manual);
let end = unsafe { talc.claim((&raw mut ARENA).cast(), ARENA.len()).unwrap() };
// do some allocator operations...

// reclaim as much of the arena as possible
let opt_new_end = unsafe { talc.truncate(end, null_mut()) };
Source

pub unsafe fn resize( &mut self, heap_end: NonNull<u8>, new_end: *mut u8, ) -> Option<NonNull<u8>>

This calles Talc::extend or Talc::truncate depending on whether new_end is higher or lower than heap_end.

This is just a convenience function.

See Talc::extend and Talc::truncate for details.

Trait Implementations§

Source§

impl<S: Source, B: Binning> Debug for Talc<S, B>

Source§

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

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

impl<S: Source + Send, B: Binning> Send for Talc<S, B>

Source§

impl<S: Source + Sync, B: Binning> Sync for Talc<S, B>

Auto Trait Implementations§

§

impl<S, B> Freeze for Talc<S, B>

§

impl<S, B> RefUnwindSafe for Talc<S, B>

§

impl<S, B> Unpin for Talc<S, B>
where <B as Binning>::AvailabilityBitField: Unpin, PhantomData<fn(B) -> B>: Unpin, S: Unpin,

§

impl<S, B> UnsafeUnpin for Talc<S, B>

§

impl<S, B> UnwindSafe for Talc<S, B>

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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.