[][src]Struct alloc_compose::Affix

pub struct Affix<Alloc, Prefix = (), Suffix = ()> {
    pub parent: Alloc,
    // some fields omitted
}

An allocator that requests some extra memory from the parent allocator for storing a prefix and/or a suffix.

The alignment of the memory block is the maximum of the alignment of Prefix and the requested alignment. This may introduce an unused padding between Prefix and the returned memory.

To get a pointer to the prefix or the suffix, the prefix() and suffix() may be called.

Performance

Generally it's faster to calculate the pointer to the prefix than the pointer to the suffix, as the extended layout of Prefix and the requested memory is needed in order to calculate the Suffix pointer. Additionally, in most cases it's recommended to use a prefix over a suffix for a more efficient use of memory. However, small prefixes blunt the alignment so if a large alignment with a small affix is needed, suffixes may be the better option.

For layouts known at compile time the compiler is able to optimize away almost all calculations.

Examples

Prefix is 12 bytes in size and has an alignment requirement of 4 bytes. Suffix is 16 bytes in size, the requested layout requires 28 bytes, both with an alignment of 8 bytes. The parent allocator returns memory blocks of 128 bytes to demonstrate the behavior on overallocating.

#![feature(allocator_api)]

use alloc_compose::{Affix, Chunk};
use std::alloc::{Layout, System};

type Prefix = [u32; 3];
type Suffix = [u64; 2];
type Alloc = Affix<Chunk<System, 128>, Prefix, Suffix>;

let layout = Layout::from_size_align(28, 8)?;

The memory layout differs depending on Prefix and Suffix:

use core::alloc::{AllocRef, AllocInit};

let mut my_alloc = Alloc::default();

// 0          12  16                          44  48              64       128
// ╞═ Prefix ══╡   ╞════ requested memory ═════╡   ╞═══ Suffix ════╡        │
// ┢┳┳┳┳┳┳┳┳┳┳┳╅┬┬┬╆┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╅┬┬╌╌╌╌┬┬┤
// ┡┻┻┻┻┻┻┻┻┻┻┻┹┴┴┴╄┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╇┻┻┻╇┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┹┴┴╌╌╌╌┴┴┘
// │               ├┄┄┄┄┄┄ layout.size() ┄┄┄┄┄┄┘   │
// │               ├┄┄┄┄┄┄┄┄┄ memory.size ┄┄┄┄┄┄┄┄┄┤
// └→ prefix()     └→ memory.ptr                   └→ suffix()
let memory = my_alloc.alloc(layout, AllocInit::Uninitialized)?;

assert_eq!(memory.size, 32);
unsafe {
    assert_eq!(Alloc::prefix(memory.ptr, layout).cast().as_ptr(), memory.ptr.as_ptr().sub(16));
    assert_eq!(Alloc::suffix(memory.ptr, layout).cast().as_ptr(), memory.ptr.as_ptr().add(32));
}

The memory between Prefix and the requested memory is unused. If there is a padding between the requested memory and the suffix, this can be used as extra memory for the allocation. The memory after Suffix is also unused as Suffix is typed. This results in 68 bytes unused memory.

If Suffix is a zero-sized type, the space after the requested memory block can be used:

use core::ptr::NonNull;

// For convenience, the suffix can be ommitted
type Alloc = Affix<Chunk<System, 128>, Prefix>;

let mut my_alloc = Alloc::default();

// 0          12  16                          44  48              64       128
// ╞═ Prefix ══╡   ╞════ requested memory ═════╡   │               │        │
// ┢┳┳┳┳┳┳┳┳┳┳┳╅┬┬┬╆┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳╍╍╍╍┳┳┪
// ┡┻┻┻┻┻┻┻┻┻┻┻┹┴┴┴╄┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╇┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╍╍╍╍┻┻┩
// │               ├┄┄┄┄┄┄ layout.size() ┄┄┄┄┄┄┘                            │
// │               ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ memory.size ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
// └→ prefix()     └→ memory.ptr
let memory = my_alloc.alloc(layout, AllocInit::Uninitialized)?;

assert_eq!(memory.size, 112);
unsafe {
    assert_eq!(Alloc::prefix(memory.ptr, layout).cast().as_ptr(), memory.ptr.as_ptr().sub(16));
    assert_eq!(Alloc::suffix(memory.ptr, layout), NonNull::dangling());
}

This results in only 4 bytes unused memory.

If Prefix is a zero-sized type, this results in a waste of memory:

type Alloc = Affix<Chunk<System, 128>, (), Suffix>;

let mut my_alloc = Alloc::default();

// 0                          28  32              48              64       128
// ╞════ requested memory ═════╡   ╞═══ Suffix ════╡               │        │
// ┢┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╅┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┼┬┬╌╌╌╌┬┬┤
// ┡┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╇┻┻┻╇┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┹┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴╌╌╌╌┴┴┘
// ├┄┄┄┄┄┄ layout.size() ┄┄┄┄┄┄┘   │
// ├┄┄┄┄┄┄┄┄┄ memory.size ┄┄┄┄┄┄┄┄┄┤
// └→ memory.ptr                   └→ suffix()
let memory = my_alloc.alloc(layout, AllocInit::Uninitialized)?;

assert_eq!(memory.size, 32);
unsafe {
    assert_eq!(Alloc::prefix(memory.ptr, layout), NonNull::dangling());
    assert_eq!(Alloc::suffix(memory.ptr, layout).cast().as_ptr(), memory.ptr.as_ptr().add(32));
}

This results in 80 bytes unused memory. As can be seen, if possible a prefix should be preferred to the suffix.

If both, Prefix and Suffix are ZSTs, this behaves like the parent allocator:

type Alloc = Affix<Chunk<System, 128>, (), ()>;

let mut my_alloc = Alloc::default();

// 0                          28  32              48              64       128
// ╞════ requested memory ═════╡   │               │               │        │
// ┢┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳┳┳┳┳┳┳┳┳┳┳┳┳┳╈┳┳╍╍╍╍┳┳┪
// ┡┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╇┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻┻╍╍╍╍┻┻┩
// ├┄┄┄┄┄┄ layout.size() ┄┄┄┄┄┄┘                                            │
// ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄ memory.size ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
// └→ memory.ptr
let memory = my_alloc.alloc(layout, AllocInit::Uninitialized)?;

assert_eq!(memory.size, 128);
unsafe {
    assert_eq!(Alloc::prefix(memory.ptr, layout), NonNull::dangling());
    assert_eq!(Alloc::suffix(memory.ptr, layout), NonNull::dangling());
}

Fields

parent: Alloc

The parent allocator to be used as backend

Implementations

impl<Alloc, Prefix, Suffix> Affix<Alloc, Prefix, Suffix>[src]

pub const fn new(parent: Alloc) -> Self[src]

pub unsafe fn prefix(ptr: NonNull<u8>, layout: Layout) -> NonNull<Prefix>[src]

Returns a pointer to the prefix.

Safety

  • ptr must denote a block of memory currently allocated via this allocator, and
  • layout must fit that block of memory.

pub unsafe fn suffix(ptr: NonNull<u8>, layout: Layout) -> NonNull<Suffix>[src]

Returns a pointer to the suffix.

Safety

  • ptr must denote a block of memory currently allocated via this allocator, and
  • layout must fit that block of memory.

Trait Implementations

impl<Alloc, Prefix, Suffix> AllocRef for Affix<Alloc, Prefix, Suffix> where
    Alloc: AllocRef
[src]

impl<Alloc: Clone, Prefix, Suffix> Clone for Affix<Alloc, Prefix, Suffix>[src]

impl<Alloc: Copy, Prefix, Suffix> Copy for Affix<Alloc, Prefix, Suffix>[src]

impl<Alloc: Debug, Prefix, Suffix> Debug for Affix<Alloc, Prefix, Suffix>[src]

impl<Alloc: Default, Prefix, Suffix> Default for Affix<Alloc, Prefix, Suffix>[src]

impl<Alloc: Eq, Prefix, Suffix> Eq for Affix<Alloc, Prefix, Suffix>[src]

impl<Alloc: PartialEq, Prefix, Suffix> PartialEq<Affix<Alloc, Prefix, Suffix>> for Affix<Alloc, Prefix, Suffix>[src]

impl<Alloc: Send, Prefix, Suffix> Send for Affix<Alloc, Prefix, Suffix>[src]

impl<Alloc: Sync, Prefix, Suffix> Sync for Affix<Alloc, Prefix, Suffix>[src]

impl<Alloc: Unpin, Prefix, Suffix> Unpin for Affix<Alloc, Prefix, Suffix>[src]

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T> ToOwned for T where
    T: Clone
[src]

type Owned = T

The resulting type after obtaining ownership.

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

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

The type returned in the event of a conversion error.