Quickly fork, edit online, and submit a pull request for this page.
Requires a signed-in GitHub account. This works well for small changes.
If you'd like to make larger changes you may want to consider using
a local clone.

std.experimental.allocator.building_blocks.bitmapped_block

BitmappedBlock implements a simple heap consisting of one contiguous area
of memory organized in blocks, each of size theBlockSize. A block is a unit
of allocation. A bitmap serves as bookkeeping data, more precisely one bit per
block indicating whether that block is currently allocated or not.

Passing NullAllocator as ParentAllocator (the default) means user code
manages allocation of the memory block from the outside; in that case
BitmappedBlock must be constructed with a void[] preallocated block and
has no responsibility regarding the lifetime of its support underlying storage.
If another allocator type is passed, BitmappedBlock defines a destructor that
uses the parent allocator to release the memory block. That makes the combination of AllocatorList, BitmappedBlock, and a back-end allocator such as MmapAllocator a simple and scalable solution for memory allocation.

There are advantages to storing bookkeeping data separated from the payload
(as opposed to e.g. using AffixAllocator to store metadata together with
each allocation). The layout is more compact (overhead is one bit per block),
searching for a free block during allocation enjoys better cache locality, and
deallocation does not touch memory around the payload being deallocated (which
is often cold).

Allocation requests are handled on a first-fit basis. Although linear in
complexity, allocation is in practice fast because of the compact bookkeeping
representation, use of simple and fast bitwise routines, and caching of the
first available block position. A known issue with this general approach is
fragmentation, partially mitigated by coalescing. Since BitmappedBlock does
not need to maintain the allocated size, freeing memory implicitly coalesces
free blocks together. Also, tuning blockSize has a considerable impact on
both internal and external fragmentation.

The size of each block can be selected either during compilation or at run
time. Statically-known block sizes are frequent in practice and yield slightly
better performance. To choose a block size statically, pass it as the blockSize parameter as in BitmappedBlock!(4096). To choose a block
size parameter, use BitmappedBlock!(chooseAtRuntime) and pass the
block size to the constructor.

If blockSize == chooseAtRuntime, BitmappedBlock offers a read/write
property blockSize. It must be set before any use of the allocator.
Otherwise (i.e. theBlockSize is a legit constant), blockSize is
an alias for theBlockSize. Whether constant or variable, must also be
a multiple of alignment. This constraint is asserted statically
and dynamically.

alias alignment = theAlignment;

The alignment offered is user-configurable statically through parameter
theAlignment, defaulted to platformAlignment.

ParentAllocator parent;

The parent allocator. Depending on whether ParentAllocator holds state
or not, this is a member variable or an alias for
ParentAllocator.instance.

this(ubyte[] data);

this(ubyte[] data, uint blockSize);

this(size_t capacity);

this(size_t capacity, uint blockSize);

Constructs a block allocator given a hunk of memory, or a desired capacity
in bytes.

If ParentAllocator is NullAllocator, only the constructor
taking data is defined and the user is responsible for freeing data if desired.

Otherwise, both constructors are defined. The data-based
constructor assumes memory has been allocated with the parent allocator.
The capacity-based constructor uses ParentAllocator to allocate
an appropriate contiguous hunk of memory. Regardless of the constructor
used, the destructor releases the memory by using ParentAllocator.deallocate.

Allocates s bytes of memory and returns it, or null if memory
could not be allocated.

The following information might be of help with choosing the appropriate
block size. Actual allocation occurs in sizes multiple of the block size.
Allocating one block is the fastest because only one 0 bit needs to be
found in the metadata. Allocating 2 through 64 blocks is the next cheapest
because it affects a maximum of two ulongs in the metadata.
Allocations greater than 64 blocks require a multiword search through the
metadata.

@safe void[] allocateFresh(const size_t s);

Allocates s bytes of memory and returns it, or null if memory
could not be allocated.

allocateFresh behaves just like allocate, the only difference being that
this always returns unused(fresh) memory. Although there may still be available
space in the BitmappedBlock, allocateFresh could still return null,
because all the available blocks have been previously deallocated.

void[] alignedAllocate(size_t n, uint a);

Allocates a block with specified alignment a. The alignment must be a
power of 2. If a <= alignment, function forwards to allocate.
Otherwise, it attempts to overallocate and then adjust the result for
proper alignment. In the worst case the slack memory is around two blocks.

void[] allocateAll();

If the BitmappedBlock object is empty (has no active allocation), allocates
all memory within and returns a slice to it. Otherwise, returns null
(i.e. no attempt is made to allocate the largest available block).

const pure nothrow @nogc @trusted Ternary owns(const void[] b);

Returns Ternary.yes if b belongs to the BitmappedBlock object,
Ternary.no otherwise. Never returns Ternary.unkown. (This
method is somewhat tolerant in that accepts an interior slice.)

A BitmappedBlock with additional structure for supporting resolveInternalPointer. To that end, BitmappedBlockWithInternalPointers adds a
bitmap (one bit per block) that marks object starts. The bitmap itself has
variable size and is allocated together with regular allocations.

The time complexity of resolveInternalPointer is Ο(k), where k
is the size of the object within which the internal pointer is looked up.

this(ubyte[] data);

this(size_t capacity);

Constructors accepting desired capacity or a preallocated buffer, similar
in semantics to those of BitmappedBlock.