Module std.experimental.allocator.building_blocks
Assembling Your Own Allocator
This package also implements
untyped composable memory allocators. They are untyped because they deal
exclusively in void[] and have no notion of what type the memory allocated
would be destined for. They are composable because the included allocators
are building blocks that can be assembled in complex nontrivial allocators.
Unlike the allocators for the C and C++ programming languages, which manage
the allocated size internally, these allocators require that the client
maintains (or knows a priori) the allocation size for each piece of memory
allocated. Put simply, the client must pass the allocated size upon
deallocation. Storing the size in the allocator has significant negative
performance implications, and is virtually always redundant because client code
needs knowledge of the allocated size in order to avoid buffer overruns. (See
more discussion in a proposal for sized
deallocation in C++.) For this reason, allocators herein traffic in void[]
as opposed to void*.
In order to be usable as an allocator, a type should implement the
following methods with their respective semantics. Only alignment and  allocate are required. If any of the other methods is missing, the allocator
is assumed to not have that capability (for example some allocators do not offer
manual deallocation of memory). Allocators should NOT implement
unsupported methods to always fail. For example, an allocator that lacks the
capability to implement alignedAllocate should not define it at all (as
opposed to defining it to always return null or throw an exception). The
missing implementation statically informs other components about the
allocator's capabilities and allows them to make design decisions accordingly.
| Method name | Semantics | 
|---|---|
| `uint alignment;` Post: `result > 0` | Returns the minimum
alignment of all data returned by the allocator. An allocator may implement alignmentas a statically-knownenumvalue only. Applications that need
dynamically-chosen alignment values should use thealignedAllocateandalignedReallocateAPIs. | 
| `size_t goodAllocSize(size_t n);` Post: `result >= n` | Allocators
customarily allocate memory in discretely-sized chunks. Therefore, a request for nbytes may result in a larger allocation. The extra memory allocated goes
unused and adds to the so-called internal fragmentation.
The functiongoodAllocSize(n)returns the actual number of bytes that would
be allocated upon a request fornbytes. This module defines a default
implementation that returnsnrounded up to a multiple of the allocator's
alignment. | 
| `void[] allocate(size_t s);` Post: `result is null || result.length == s` | If s == 0, the call may return any empty slice (includingnull). Otherwise, the call allocatessbytes of memory and returns the
allocated block, ornullif the request could not be satisfied. | 
| `void[] alignedAllocate(size_t s, uint a);`uint a);, Post: `result is null || result.length == s` | Similar to allocate, with the additional
guarantee that the memory returned is aligned to at leastabytes.amust be a power of 2. | 
| `void[] allocateAll();` | Offers all of allocator's memory to the
caller, so it's usually defined by fixed-size allocators. If the allocator is
currently NOT managing any memory, then allocateAll()shall allocate and
return all memory available to the allocator, and subsequent calls to all
allocation primitives should not succeed (e.g.allocateshall returnnulletc). Otherwise,allocateAllonly works on a best-effort basis, and
the allocator is allowed to returnnulleven if does have available memory.
Memory allocated withallocateAllis not otherwise special (e.g. can be
reallocated or deallocated with the usual primitives, if defined). | 
| `bool expand(ref void[] b, size_t delta);`size_t delta);, Post: `!result || b.length == old(b).length + delta` | Expands bbydeltabytes. Ifdelta == 0, succeeds without changingb. Ifb is null, returnsfalse(the null pointer cannot be expanded in place). Otherwise,bmust be a buffer previously allocated with the same allocator. If expansion
was successful,expandchangesb's length toband
returnstrue. Upon failure, the call effects no change upon the allocator
object, leavesbunchanged, and returnsfalse. | 
| `bool reallocate(ref void[] b, size_t s);`size_t s);, Post: `!result || b.length == s` | Reallocates bto sizes, possibly moving memory around.bmust benullor a buffer allocated with the same allocator. If
reallocation was successful,reallocatechangesbappropriately and
returnstrue. Upon failure, the call effects no change upon the allocator
object, leavesbunchanged, and returnsfalse. An allocator should
implementreallocateif it can derive some advantage from doing so;
otherwise, this module defines areallocatefree function implemented in
terms ofexpand,allocate, anddeallocate. | 
| `bool alignedReallocate(ref void[] b, size_t s, uint a);` size_t s, uint a);, Post: `!result || b.length == s` | Similar to reallocate, but guarantees the
reallocated memory is aligned atabytes. The buffer must have been
originated with a call toalignedAllocate.amust be a power of 2
greater than(void*). An allocator should implementalignedReallocateif it can derive some advantage from doing so; otherwise,
this module defines aalignedReallocatefree function implemented in terms
ofexpand,alignedAllocate, anddeallocate. | 
| `Ternary owns(void[] b);` | Returns Ternaryifbhas been
allocated with this allocator. An allocator should define this method only if it
can decide on ownership precisely and fast (in constant time, logarithmic time,
or linear time with a low multiplication factor). Traditional allocators such as
the C heap do not define such functionality. Ifb is null, the allocator
shall returnTernary, i.e. no allocator owns thenullslice. | 
| `Ternary resolveInternalPointer(void* p, ref void[] result);`ref void[] result); | If pis a pointer somewhere inside a block allocated with this allocator,resultholds a pointer to the beginning of the allocated block and returnsTernary. Otherwise,resultholdsnulland returnsTernary.
If the pointer points immediately after an allocated block, the result is
implementation defined. | 
| `bool deallocate(void[] b);` | If b is null, does
nothing and returnstrue. Otherwise, deallocates memory previously allocated
with this allocator and returnstrueif successful,falseotherwise. An
implementation that would not support deallocation (i.e. would always returnfalseshould not define this primitive at all.) | 
| `bool deallocateAll();` Post: `empty` | Deallocates all memory allocated with this allocator. If an allocator implements this method, it must specify whether its destructor calls it, too. | 
| `Ternary empty();` | Returns Ternaryif and only if the
allocator holds no memory (i.e. no allocation has occurred, or all allocations
have been deallocated). | 
| `static Allocator instance;` Post: `instance is a valid Allocator object` | Some allocators are monostate, i.e. have only
an instance and hold only global state. (Notable examples are C's own malloc-based allocator and D's garbage-collected heap.) Such allocators must
define a staticinstanceinstance that serves as the symbolic placeholder
for the global instance of the allocator. An allocator should not hold state
and defineinstancesimultaneously. Depending on whether the allocator is
thread-safe or not, this instance may beshared. | 
Sample Assembly
The example below features an allocator modeled after jemalloc, which uses a battery of free-list allocators spaced so as to keep
internal fragmentation to a minimum. The FList definitions specify no
bounds for the freelist because the Segregator does all size selection in
advance.
Sizes through 3584 bytes are handled via freelists of staggered sizes. Sizes
from 3585 bytes through 4072 KB are handled by a BitmappedBlock with a
block size of 4 KB. Sizes above that are passed direct to the GCAllocator.
alias FList = FreeList!(GCAllocator, 0, unbounded);
alias A = Segregator!(
    8, FreeList!(GCAllocator, 0, 8),
    128, Bucketizer!(FList, 1, 128, 16),
    256, Bucketizer!(FList, 129, 256, 32),
    512, Bucketizer!(FList, 257, 512, 64),
    1024, Bucketizer!(FList, 513, 1024, 128),
    2048, Bucketizer!(FList, 1025, 2048, 256),
    3584, Bucketizer!(FList, 2049, 3584, 512),
    4072 * 1024, AllocatorList!(
        () => BitmappedBlock!(GCAllocator, 4096)(4072 * 1024)),
    GCAllocator
);
A tuMalloc;
auto b = tuMallocAllocating memory for sharing across threads
One allocation pattern used in multithreaded applications is to share memory across threads, and to deallocate blocks in a different thread than the one that allocated it.
All allocators in this module accept and return void[] (as opposed to
shared void[]). This is because at the time of allocation, deallocation, or
reallocation, the memory is effectively not shared (if it were, it would
reveal a bug at the application level).
The issue remains of calling a from a different thread than
the one that allocated b. It follows that both threads must have access to
the same instance a of the respective allocator type. By definition of D,
this is possible only if a has the shared qualifier. It follows that
the allocator type must implement allocate and deallocate as shared methods. That way, the allocator commits to allowing usable shared
instances.
Conversely, allocating memory with one non-shared allocator, passing it
across threads (by casting the obtained buffer to shared), and later
deallocating it in a different thread (either with a different allocator object
or with the same allocator object after casting it to shared) is illegal.
Building Blocks
The table below gives a synopsis of predefined allocator building blocks,
with their respective modules. Either import the needed modules individually,
or import std, which imports them all
publicly. The building blocks can be assembled in unbounded ways and also
combined with your own. For a collection of typical and useful preassembled
allocators and for inspiration in defining more such assemblies, refer to
std.
| Allocator | Description | 
|---|---|
| `std.experimental.allocator.building_blocks.null_allocator` | Very good at doing absolutely nothing. A good starting point for defining other allocators or for studying the API. | 
| `std.experimental.allocator.gc_allocator` | The system-provided garbage-collector allocator.
This should be the default fallback allocator tapping into system memory. It
offers manual freeand dutifully collects litter. | 
| `std.experimental.allocator.mallocator` | The C heap allocator, a.k.a. malloc/realloc/free. Use sparingly and only for code that is unlikely
to leak. | 
| `std.experimental.allocator.mallocator` | Interface to OS-specific allocators that
support specifying alignment: posix_memalignon Posix and__aligned_xxxon Windows. | 
| `std.experimental.allocator.building_blocks.aligned_block_list` | A wrapper around a list of allocators which allow for very fast deallocations. | 
| `std.experimental.allocator.building_blocks.affix_allocator` | Allocator that allows and manages allocating extra prefix and/or a suffix bytes for each block allocated. | 
| `std.experimental.allocator.building_blocks.bitmapped_block` | Organizes one contiguous chunk of memory in equal-size blocks and tracks allocation status at the cost of one bit per block. | 
| `std.experimental.allocator.building_blocks.fallback_allocator` | Allocator that combines two other allocators - primary and fallback. Allocation requests are first tried with primary, and upon failure are passed to the fallback. Useful for small and fast allocators fronting general-purpose ones. | 
| `std.experimental.allocator.building_blocks.free_list` | Allocator that implements a free list on top of any other allocator. The preferred size, tolerance, and maximum elements are configurable at compile- and run time. | 
| `std.experimental.allocator.building_blocks.free_list` | Same features as FreeList, but packaged as
asharedstructure that is accessible to several threads. | 
| `std.experimental.allocator.building_blocks.free_tree` | Allocator similar to FreeListthat uses a
binary search tree to adaptively store not one, but many free lists. | 
| `std.experimental.allocator.building_blocks.region` | Region allocator organizes a chunk of memory as a simple bump-the-pointer allocator. | 
| `std.experimental.allocator.building_blocks.region` | Region holding its own allocation, most often on the stack. Has statically-determined size. | 
| `std.experimental.allocator.building_blocks.region` | Region using sbrkfor allocating memory. | 
| `std.experimental.allocator.mmap_allocator` | Allocator using mmapdirectly. | 
| `std.experimental.allocator.building_blocks.stats_collector` | Collect statistics about any other allocator. | 
| `std.experimental.allocator.building_blocks.quantizer` | Allocates in coarse-grained quantas, thus improving performance of reallocations by often reallocating in place. The drawback is higher memory consumption because of allocated and unused memory. | 
| `std.experimental.allocator.building_blocks.allocator_list` | Given an allocator factory, lazily creates as many allocators as needed to satisfy allocation requests. The allocators are stored in a linked list. Requests for allocation are satisfied by searching the list in a linear manner. | 
| `std.experimental.allocator.building_blocks.segregator` | Segregates allocation requests by size and dispatches them to distinct allocators. | 
| `std.experimental.allocator.building_blocks.bucketizer` | Divides allocation sizes in discrete buckets and uses an array of allocators, one per bucket, to satisfy requests. | 
| `std.experimental.allocator.building_blocks.ascending_page_allocator` | A memory safe allocator where sizes are rounded to a multiple of the page size and allocations are satisfied at increasing addresses. |