Pool structv0.1.0

Source
An allocator that serves small blocks from per-class free lists.
pub struct Pool {
    // private fields
}

Move-only, because it owns its chunks. Pass it to something expecting an Allocator through Handle, which borrows rather than moves.

Constructors

Pool

pub func Pool(
    backing: Allocator,
    blocksPerChunk: uint
) -> Pool

A pool that will take its chunks from backing. backing is borrowed for the call and kept. blocksPerChunk is how many blocks each chunk is carved into; zero asks for the default. Cannot fail and allocates nothing: the first chunk is taken when the first request arrives, so a pool that is never used costs nothing.

NameTypeDescription
backingAllocatorwhere the pool's chunks come from
blocksPerChunkuinthow many blocks a chunk carves into; zero asks for the default

Returns: the pool, holding no chunk yet

Source

Methods

Handle

pub func Handle(self: *var Pool) -> PoolHandle

A copyable interface adapter pointing at this pool. self stays the caller's. Allocations through the handle update this pool rather than copied state. Cannot fail. Runs in constant time and allocates nothing. The handle borrows the pool and must not outlive it, nor survive the pool being moved.

Returns: a copyable adapter pointing at this pool

Source

Reset

pub func Reset(self: &var Pool)

Takes every pooled block back at once, keeping the chunks for the next round of work. self is borrowed mutably. Every pooled pointer the pool handed out becomes invalid, including any a caller still holds. The chunks are kept and carved again, so a second round of the same work asks the backing allocator for nothing. Blocks that went to the backing allocator because they were too large or too strictly aligned are not touched, and are still the caller's to release. Runs in time proportional to the number of blocks. Cannot fail. Nothing may be read from or written to any pooled allocation afterwards.

Source

LiveBlocks

pub func LiveBlocks(self: &Pool) -> uint

How many pooled blocks are currently handed out. self is borrowed. Blocks that went to the backing allocator are not counted, because the pool does not track them.

Returns: how many blocks are currently handed out

Source

ChunkCount

pub func ChunkCount(self: &Pool) -> uint

How many chunks the pool has taken from its backing allocator. self is borrowed. Zero before the first request.

Returns: how many chunks the pool holds

Source

ClassSize

pub func ClassSize(self: &Pool, index: uint) -> uint

The block size of class index, or zero for an index past the last class. self is borrowed. Classes run from PoolSmallestClass to PoolLargestClass, doubling.

NameTypeDescription
indexuintwhich size class

Returns: the block size that class serves

Source

Destructor

func ~Pool(self: &var Pool)

Gives every chunk back to the backing allocator. Runs when the pool's life ends. Every pooled pointer the pool handed out becomes invalid. Blocks that went to the backing allocator directly are not released here and are still the caller's.

Source

PoolClassCount

pub const PoolClassCount: uint = 5;

How many size classes a pool serves.

Source

PoolSmallestClass

pub const PoolSmallestClass: uint = 16;

The smallest size class, and so the block a one-byte request occupies. Large enough to hold the link a free block stores in its own storage, which is what lets a free list cost nothing beyond the blocks already on it.

Source

PoolLargestClass

pub const PoolLargestClass: uint = 256;

The largest size class. A request past this goes to the backing allocator instead.

Source

PoolAlignment

pub const PoolAlignment: uint = 16;

The alignment every chunk is taken at, and so the strictest alignment a pooled block can satisfy. Every class is a multiple of this, so every block carved from a chunk is aligned to it as well.

Source

DefaultPoolBlocksPerChunk

pub const DefaultPoolBlocksPerChunk: uint = 32;

How many blocks a chunk holds when the caller does not say.

Source

PoolChunk

pub struct PoolChunk {
    // private fields
}

One chunk of pool storage, carved into blocks of a single class. Never constructed by a caller. The blocks follow the header, and the header records what the chunk was taken with so that releasing or re-carving it needs nothing else.

Source

PoolHandle

pub struct PoolHandle {
    // private fields
}

A copyable interface adapter pointing at a pool. A handle is cheap to copy and may be stored wherever an allocator is wanted, but it does not own the pool.

Source

Implements Allocator

PoolHandle conforms to Allocator.

Allocate

pub func Allocate(
    self: &PoolHandle,
    layout: Layout
) -> (*var opaque) ! AllocError

Takes a block of the smallest class that holds layout. result receives the address when this reports None. A zero-sized layout gives the shared empty block. A layout too large for the largest class, or needing an alignment stricter than PoolAlignment, is passed to the backing allocator unchanged. Takes a chunk when the class has no free block, which is the only path here that can fail. Otherwise runs in constant time. The block lives until it is released, the pool is reset, or the pool is destroyed.

NameTypeDescription
layoutLayoutthe size and alignment wanted

Returns: the address of the storage, or why none was taken

Source

Deallocate

pub func Deallocate(
    self: &PoolHandle,
    block: *var opaque,
    layout: Layout
) -> ! AllocError

Puts a block back on the free list of the class its layout names. layout must equal the one the block was taken with, which is what tells the pool whether the block is its own or the backing allocator's. A block that went to the backing allocator is released there. Runs in constant time, and cannot fail for a pooled block. The block may be handed to the next allocation of its class at once.

NameTypeDescription
block*var opaquean address this allocator returned
layoutLayoutthe layout it was allocated with

Returns: nothing when the release was accepted, or why it was refused

Source

Reallocate

pub func Reallocate(
    self: &PoolHandle,
    block: *var opaque,
    oldLayout: Layout,
    newLayout: Layout
) -> (*var opaque) ! AllocError

Changes the size of a block, which costs nothing when both sizes land in the same class. The two layouts must share an alignment. A block whose new size fits the class it already occupies comes back unmoved — growing a 20-byte allocation to 30 does not touch storage at all. Anything else takes a new block, copies, and releases the old one.

NameTypeDescription
block*var opaquethe existing allocation's address
oldLayoutLayoutthe layout block was allocated with
newLayoutLayoutthe layout wanted, which must have the same alignment

Returns: the address the block is at afterwards, or why it was not resized

Source

ClassIndexFor

pub func ClassIndexFor(layout: Layout) -> uint

The class a layout belongs to, or PoolClassCount for a layout no class can serve. layout is read by value. A size past PoolLargestClass, or an alignment stricter than PoolAlignment, has no class, and the caller passes it to the backing allocator instead. The answer depends only on the layout, which is what lets a release reach the same conclusion as the allocation it matches without the pool recording anything per block. Cannot fail. Runs in time proportional to the number of classes and allocates nothing.

Source

Parameters

NameTypeDescription
layoutLayoutthe request to classify

Returns: the index of the class that serves it, or the class count when none does