stdx.allocator
High-level interface for allocators. Implements bundled allocation/creation
and destruction/deallocation of data including struct
s and class
es,
and also array primitives related to allocation. This module is the entry point
for both making use of allocators and for their documentation.
Discussion
Synopsis:
// Allocate an int, initialize it with 42
int* p = theAllocator.make!int(42);
assert(*p == 42);
// Destroy and deallocate it
theAllocator.dispose(p);
// Allocate using the global process allocator
p = processAllocator.make!int(100);
assert(*p == 100);
// Destroy and deallocate
processAllocator.dispose(p);
// Create an array of 50 doubles initialized to -1.0
double[] arr = theAllocator.makeArray!double(50, -1.0);
// Append two zeros to it
theAllocator.expandArray(arr, 2, 0.0);
// On second thought, take that back
theAllocator.shrinkArray(arr, 2);
// Destroy and deallocate
theAllocator.dispose(arr);
Layered Structure
D's allocators have a layered structure in both implementation and documentation:
- A high-level, dynamically-typed layer (described further down in this
module). It consists of an interface called , which concret;
allocators need to implement. The interface primitives themselves are oblivious
to the type of the objects being allocated; they only deal in
void[]
, by necessity of the interface being dynamic (as opposed to type-parameterized). Each thread has a currentallocator
it uses by default, which is a thread-local variable of type . The process has a global allocator called , also of type . When a new thread is created, is copied into . An application can change the objects to which these references point. By default, at application startup, refers to an object that uses D's garbage collected heap. This layer also include high-level functions such as and that comfortably allocate/create and respectively destroy/deallocate objects. This layer is all needed for most casual uses of allocation primitives. - A mid-level, statically-typed layer for assembling several allocators into one. It uses properties of the type of the objects being created to route allocation requests to possibly specialized allocators. This layer is relatively thin and implemented and documented in the module. It allows an interested user to e.g. use different allocators for arrays versus fixed-sized objects, to the end of better overall performance.
- A low-level collection of highly generic heap building blocks
Lego-like pieces that can be used to assemble application-specific allocators.
The real allocation smarts are occurring at this level. This layer is of
interest to advanced applications that want to configure their own allocators.
A good illustration of typical uses of these building blocks is module which defines a collection of frequently-
used preassembled
allocator
objects. The implementation and documentation entry point is . By design, the primitives of the static interface have the same signatures as the primitives but are for the most part optional and driven by static introspection. The parameterized class offers an immediate and useful means to package a static low-level allocator into an implementation of . - Core allocator objects that interface with D's garbage collected heap
(), the C
malloc
family (), and the OS (). Most custom allocators would ultimately obtain memory from one of these core allocators.
Idiomatic Use of
As of this time, is not integrated with D's built-in operators that allocate memory, such as
new
, array literals, or
array concatenation operators. That means is
opt-inapplications need to make explicit use of it.
For casual creation and disposal of dynamically-allocated objects, use , , and the array-specific functions , , and . These use by default D's garbage collected heap, but open the application to better configuration options. These primitives work either with
theAllocator
but also with any allocator
obtained
by combining heap building blocks. For example:
void fun(size_t n) { // Use the current allocator int[] a1 = theAllocator.makeArray!int(n); scope(exit) theAllocator.dispose(a1); ... }
To experiment with alternative allocators, set for the current thread. For example, consider an application that allocates many 8-byte objects. These are not well supported by the default allocator, so a would be recommended. To install one in
main
, the application would use:
void main() { import stdx.allocator.building_blocks.free_list : FreeList; theAllocator = allocatorObject(FreeList!8()); ... }
Saving the IAllocator
Reference For Later Use
As with any global resource, setting
theAllocator
and processAllocator
should not be done often and casually. In particular, allocating memory with
one allocator
and deallocating with another causes undefined behavior.
Typically, these variables are set during application initialization phase and
last through the application.
To avoid this, long-lived objects that need to perform allocations, reallocations, and deallocations relatively often may want to store a reference to the allocator object they use throughout their lifetime. Then, instead of using
theAllocator
for internal allocation-related tasks, they'd use the
internally held reference. For example, consider a user-defined hash table:
struct HashTable { private IAllocator _allocator; this(size_t buckets, IAllocator allocator = theAllocator) { this._allocator = allocator; ... } // Getter and setter IAllocator allocator() { return _allocator; } void allocator(IAllocator a) { assert(empty); _allocator = a; } }
Following initialization, the
HashTable
object would consistently use its
object for acquiring memory. Furthermore, setting
to point to a different allocator should be legal but
only if the object is empty; otherwise, the object wouldn't be able to
deallocate its existing state.
Using Allocators without IAllocator
Allocators assembled from the heap building blocks don't need to go through
IAllocator
to be usable. They have the same primitives as IAllocator
and
they work with , , etc. So it
suffice to create allocator
objects wherever fit and use them appropriately:
void fun(size_t n) { // Use a stack-installed allocator for up to 64KB StackFront!65536 myAllocator; int[] a2 = myAllocator.makeArray!int(n); scope(exit) myAllocator.dispose(a2); ... }
In this case,
myAllocator
does not obey the IAllocator
interface, but
implements its primitives so it can work with makeArray
by means of duck
typing.
One important thing to note about this setup is that statically-typed assembled allocators are almost always faster than allocators that go through
IAllocator
. An important rule of thumb is: "assemble allocator
first, adapt
to IAllocator
after". A good allocator
implements intricate logic by means of
template assembly, and gets wrapped with IAllocator
(usually by means of
) only once, at client level.
License
.
Source:
-
Declaration
interface
IAllocator
;Dynamic allocator interface. Code that defines allocators ultimately implements this interface. This should be used wherever a uniform type is required for encapsulating various allocator implementations.
Discussion
Composition of allocators is not recommended at this level due to inflexibility of dynamic interfaces and inefficiencies caused by cascaded multiple calls. Instead, compose allocators using the static interface defined in , then adapt the composed allocator to
(possibly by using below).IAllocator
Methods returning return upon success, upon failure, and if the primitive is not implemented by the allocator instance.-
Declaration
abstract @property uint
alignment
();Returns the
alignment
offered. -
Declaration
abstract size_t
goodAllocSize
(size_ts
);Returns the good allocation size that guarantees zero internal fragmentation.
-
Declaration
abstract void[]
allocate
(size_t, TypeInfoti
= null);Allocates
n
bytes of memory. -
Declaration
abstract void[]
alignedAllocate
(size_tn
, uinta
);Allocates
bytes of memory with specified alignmentn
. Implementations that do not support this primitive should always returna
null
. -
Declaration
abstract void[]
allocateAll
();Allocates and returns all memory available to this allocator. Implementations that do not support this primitive should always return
null
. -
Declaration
abstract bool
expand
(ref void[], size_t);Expands a memory block in place and returns
true
if successful. Implementations that don't support this primitive should always returnfalse
. -
Declaration
abstract bool
reallocate
(ref void[], size_t);Reallocates a memory block.
-
Declaration
abstract bool
alignedReallocate
(ref void[]b
, size_tsize
, uintalignment
);Reallocates a memory block with specified
alignment
. -
Declaration
abstract Ternary
owns
(void[]b
);Returns if the allocator
owns
, if the allocator doesn't own , and if ownership cannot be determined. Implementations that don't support this primitive should always returnTernary.unknown
. -
Declaration
abstract Ternary
resolveInternalPointer
(const void*p
, ref void[]result
);Resolves an internal pointer to the full block allocated. Implementations that don't support this primitive should always return
Ternary.unknown
. -
Declaration
abstract bool
deallocate
(void[]b
);Deallocates a memory block. Implementations that don't support this primitive should always return
false
. A simple way to check that an allocator supports deallocation is to call . -
Declaration
abstract bool
deallocateAll
();Deallocates all memory. Implementations that don't support this primitive should always return
false
. -
Declaration
abstract Ternary
empty
();Returns if no memory is currently allocated from this allocator, if some allocations are currently active, or if not supported.
-
-
Declaration
interface
ISharedAllocator
;Dynamic shared allocator interface. Code that defines allocators shareable across threads ultimately implements this interface. This should be used wherever a uniform type is required for encapsulating various allocator implementations.
Discussion
Composition of allocators is not recommended at this level due to inflexibility of dynamic interfaces and inefficiencies caused by cascaded multiple calls. Instead, compose allocators using the static interface defined in , then adapt the composed allocator to
(possibly by using below).ISharedAllocator
Methods returning return upon success, upon failure, and if the primitive is not implemented by the allocator instance.-
Declaration
abstract shared @property uint
alignment
();Returns the
alignment
offered. -
Declaration
abstract shared size_t
goodAllocSize
(size_ts
);Returns the good allocation size that guarantees zero internal fragmentation.
-
Declaration
abstract shared void[]
allocate
(size_t, TypeInfoti
= null);Allocates
n
bytes of memory. -
Declaration
abstract shared void[]
alignedAllocate
(size_tn
, uinta
);Allocates
bytes of memory with specified alignmentn
. Implementations that do not support this primitive should always returna
null
. -
Declaration
abstract shared void[]
allocateAll
();Allocates and returns all memory available to this allocator. Implementations that do not support this primitive should always return
null
. -
Declaration
abstract shared bool
expand
(ref void[], size_t);Expands a memory block in place and returns
true
if successful. Implementations that don't support this primitive should always returnfalse
. -
Declaration
abstract shared bool
reallocate
(ref void[], size_t);Reallocates a memory block.
-
Declaration
abstract shared bool
alignedReallocate
(ref void[]b
, size_tsize
, uintalignment
);Reallocates a memory block with specified
alignment
. -
Declaration
abstract shared Ternary
owns
(void[]b
);Returns if the allocator
owns
, if the allocator doesn't own , and if ownership cannot be determined. Implementations that don't support this primitive should always returnTernary.unknown
. -
Declaration
abstract shared Ternary
resolveInternalPointer
(const void*p
, ref void[]result
);Resolves an internal pointer to the full block allocated. Implementations that don't support this primitive should always return
Ternary.unknown
. -
Declaration
abstract shared bool
deallocate
(void[]b
);Deallocates a memory block. Implementations that don't support this primitive should always return
false
. A simple way to check that an allocator supports deallocation is to call . -
Declaration
abstract shared bool
deallocateAll
();Deallocates all memory. Implementations that don't support this primitive should always return
false
. -
Declaration
abstract shared Ternary
empty
();Returns if no memory is currently allocated from this allocator, if some allocations are currently active, or if not supported.
-
-
Declaration
nothrow @nogc @property @safe IAllocator
theAllocator
();
nothrow @nogc @property @safe voidtheAllocator
(IAllocatora
);Gets/sets the allocator for the current thread. This is the default allocator that should be used for allocating thread-local memory. For allocating memory to be shared across threads, use (below). By default, ultimately fetches memory from , which in turn uses the garbage collected heap.
Examples
// Install a new allocator that is faster for 128-byte allocations. import stdx.allocator.building_blocks.free_list : FreeList; import stdx.allocator.gc_allocator : GCAllocator; auto oldAllocator = theAllocator; scope(exit) theAllocator = oldAllocator; theAllocator = allocatorObject(FreeList!(GCAllocator, 128)()); // Use the now changed allocator to allocate an array const ubyte[] arr = theAllocator.makeArray!ubyte(128); assert(arr.ptr); //...
-
Declaration
@property shared(ISharedAllocator)
processAllocator
();
@property voidprocessAllocator
(shared ISharedAllocatora
);Gets/sets the allocator for the current process. This allocator must be used for allocating memory shared across threads. Objects created using this allocator can be cast to .
-
Declaration
auto
make
(T, Allocator, A...)(auto ref Allocatoralloc
, auto ref Aargs
);Dynamically allocates (using ) and then creates in the memory allocated an object of type , using (if any) for its initialization. Initialization occurs in the memory allocated and is otherwise semantically the same as . (Note that using creates a pointer to an (empty) array of s, not an array. To use an allocator to allocate and initialize an array, use described below.)
Parameters
T
Type of the object being created.
Allocator
alloc
The allocator used for getting the needed memory. It may be an object implementing the static interface for allocators, or an reference.
A
args
Optional arguments used for initializing the created object. If not present, the object is default constructed.
Return Value
If is a class type, returns a reference to the created object. Otherwise, returns a pointing to the created object. In all cases, returns if allocation failed.
Throws
If 's constructor throws, deallocates the allocated memory and propagates the exception.
Examples
// Dynamically allocate one integer const int* p1 = theAllocator.make!int; // It's implicitly initialized with its .init value assert(*p1 == 0); // Dynamically allocate one double, initialize to 42.5 const double* p2 = theAllocator.make!double(42.5); assert(*p2 == 42.5); // Dynamically allocate a struct static struct Point { int x, y, z; } // Use the generated constructor taking field values in order const Point* p = theAllocator.make!Point(1, 2); assert(p.x == 1 && p.y == 2 && p.z == 0); // Dynamically allocate a class object static class Customer { uint id = uint.max; this() {} this(uint id) { this.id = id; } // ... } Customer cust = theAllocator.make!Customer; assert(cust.id == uint.max); // default initialized cust = theAllocator.make!Customer(42); assert(cust.id == 42); // explicit passing of outer pointer static class Outer { int x = 3; class Inner { auto getX() { return x; } } } auto outer = theAllocator.make!Outer(); auto inner = theAllocator.make!(Outer.Inner)(outer); assert(outer.x == inner.getX);
-
Declaration
T[]
makeArray
(T, Allocator)(auto ref Allocatoralloc
, size_tlength
);
T[]makeArray
(T, Allocator)(auto ref Allocatoralloc
, size_tlength
, auto ref Tinit
);
Unqual!(ElementEncodingType!R)[]makeArray
(Allocator, R)(auto ref Allocatoralloc
, Rrange
) if (isInputRange!R && !isInfinite!R);
T[]makeArray
(T, Allocator, R)(auto ref Allocatoralloc
, Rrange
) if (isInputRange!R && !isInfinite!R);Create an array of with elements using . The array is either default-initialized, filled with copies of , or initialized with values fetched from
.range
Parameters
T
element type of the array being created
Allocator
alloc
the allocator used for getting memory
size_t
length
length
of the newly created arrayT
init
element used for filling the array
R
range
range
used for initializing the array elementsReturn Value
The newly-created array, or if either was or allocation failed.
Throws
The first two overloads throw only if
's primitives do. The overloads that involve copy initialization deallocate memory and propagate the exception if the copy operation throws.alloc
Examples
import std.algorithm.comparison : equal; static void test(T)() { T[] a = theAllocator.makeArray!T(2); assert(a.equal([0, 0])); a = theAllocator.makeArray!T(3, 42); assert(a.equal([42, 42, 42])); import std.range : only; a = theAllocator.makeArray!T(only(42, 43, 44)); assert(a.equal([42, 43, 44])); } test!int(); test!(shared int)(); test!(const int)(); test!(immutable int)();
-
Declaration
bool
expandArray
(T, Allocator)(auto ref Allocatoralloc
, ref T[]array
, size_tdelta
);
boolexpandArray
(T, Allocator)(auto ref Allocatoralloc
, ref T[]array
, size_tdelta
, auto ref Tinit
);
boolexpandArray
(T, Allocator, R)(auto ref Allocatoralloc
, ref T[]array
, Rrange
) if (isInputRange!R);Grows by appending more elements. The needed memory is allocated using . The extra elements added are either default- initialized, filled with copies of , or initialized with values fetched from
.range
Parameters
T
element type of the
array
being createdAllocator
alloc
the allocator used for getting memory
T[]
array
a reference to the
array
being grownsize_t
delta
number of elements to add (upon success the new length of is )
T
init
element used for filling the
array
R
range
range
used for initializing thearray
elementsReturn Value
upon success, if memory could not be allocated. In the latter case is left unaffected.
Throws
The first two overloads throw only if
's primitives do. The overloads that involve copy initialization deallocate memory and propagate the exception if the copy operation throws.alloc
Examples
auto arr = theAllocator.makeArray!int([1, 2, 3]); assert(theAllocator.expandArray(arr, 2)); assert(arr == [1, 2, 3, 0, 0]); import std.range : only; assert(theAllocator.expandArray(arr, only(4, 5))); assert(arr == [1, 2, 3, 0, 0, 4, 5]);
-
Declaration
bool
shrinkArray
(T, Allocator)(auto ref Allocatoralloc
, ref T[]array
, size_tdelta
);Shrinks an
array
by elements.Discussion
If , does nothing and returns
false
. Otherwise, destroys the last elements in thearray
and then reallocates thearray
's buffer. If reallocation fails, fills thearray
with default-initialized data.Parameters
T
element type of the
array
being createdAllocator
alloc
the allocator used for getting memory
T[]
array
a reference to the
array
being shrunksize_t
delta
number of elements to remove (upon success the new length of is )
Return Value
true
upon success,false
if memory could not be reallocated. In the latter case, the slice is left with default-initialized elements.Throws
The first two overloads throw only if
's primitives do. The overloads that involve copy initialization deallocate memory and propagate the exception if the copy operation throws.alloc
Examples
int[] a = theAllocator.makeArray!int(100, 42); assert(a.length == 100); assert(theAllocator.shrinkArray(a, 98)); assert(a.length == 2); assert(a == [42, 42]);
-
Declaration
void
dispose
(A, T)(auto ref Aalloc
, auto ref T*p
);
voiddispose
(A, T)(auto ref Aalloc
, auto ref Tp
) if (is(T == class) || is(T == interface));
voiddispose
(A, T)(auto ref Aalloc
, auto ref T[]array
);Destroys and then deallocates (using ) the object pointed to by a pointer, the class object referred to by a or reference, or an entire
array
. It is assumed the respective entities had been allocated with the same allocator. -
Declaration
auto
makeMultidimensionalArray
(T, Allocator, size_t N)(auto ref Allocatoralloc
, size_t[N]lengths
...);Allocates a multidimensional array of elements of type T.
Parameters
N
number of dimensions
T
element type of an element of the multidimensional arrat
Allocator
alloc
the allocator used for getting memory
size_t[N]
lengths
static array containing the size of each dimension
Return Value
An N-dimensional array with individual elements of type T.
Examples
import stdx.allocator.mallocator : Mallocator; auto mArray = Mallocator.instance.makeMultidimensionalArray!int(2, 3, 6); // deallocate when exiting scope scope(exit) { Mallocator.instance.disposeMultidimensionalArray(mArray); } assert(mArray.length == 2); foreach (lvl2Array; mArray) { assert(lvl2Array.length == 3); foreach (lvl3Array; lvl2Array) assert(lvl3Array.length == 6); }
-
Declaration
void
disposeMultidimensionalArray
(T, Allocator)(auto ref Allocatoralloc
, auto ref T[]array
);Destroys and then deallocates a multidimensional
array
, assuming it was created with makeMultidimensionalArray and the same allocator was used.Parameters
T
element type of an element of the multidimensional
array
Allocator
alloc
the allocator used for getting memory
T[]
array
the multidimensional
array
that is to be deallocatedExamples
struct TestAllocator { import stdx.allocator.common : platformAlignment; import stdx.allocator.mallocator : Mallocator; alias allocator = Mallocator.instance; private static struct ByteRange { void* ptr; size_t length; } private ByteRange[] _allocations; enum uint alignment = platformAlignment; void[] allocate(size_t numBytes) { auto ret = allocator.allocate(numBytes); _allocations ~= ByteRange(ret.ptr, ret.length); return ret; } bool deallocate(void[] bytes) { import std.algorithm.mutation : remove; import std.algorithm.searching : canFind; bool pred(ByteRange other) { return other.ptr == bytes.ptr && other.length == bytes.length; } assert(_allocations.canFind!pred); _allocations = _allocations.remove!pred; return allocator.deallocate(bytes); } ~this() { assert(!_allocations.length); } } TestAllocator allocator; auto mArray = allocator.makeMultidimensionalArray!int(2, 3, 5, 6, 7, 2); allocator.disposeMultidimensionalArray(mArray);
-
Declaration
CAllocatorImpl!A
allocatorObject
(A)(auto ref Aa
) if (!isPointer!A);
CAllocatorImpl!(A, Yes.indirect)allocatorObject
(A)(A*pa
);Returns
a
dynamically-typed built arounda
given statically- typed allocator of type . Passinga
pointer to the allocator createsa
dynamic allocator around the allocator pointed to by the pointer, without attempting to copy or move it. Passing the allocator by value or reference behaves as follows.Discussion
- If has no state, the resulting object is allocated in static shared storage.
- If has state and is copyable, the result will store
a
copy of it within. The result itself is allocated in its own statically-typed allocator. - If has state and is not copyable, the result will move the passed-in argument into the result. The result itself is allocated in its own statically-typed allocator.
Examples
import stdx.allocator.mallocator : Mallocator; IAllocator a = allocatorObject(Mallocator.instance); auto b = a.allocate(100); assert(b.length == 100); assert(a.deallocate(b)); // The in-situ region must be used by pointer import stdx.allocator.building_blocks.region : InSituRegion; auto r = InSituRegion!1024(); a = allocatorObject(&r); b = a.allocate(200); assert(b.length == 200); // In-situ regions can deallocate the last allocation assert(a.deallocate(b));
-
Declaration
shared(CSharedAllocatorImpl!A)
sharedAllocatorObject
(A)(auto ref Aa
) if (!isPointer!A);
shared(CSharedAllocatorImpl!(A, Yes.indirect))sharedAllocatorObject
(A)(A*pa
);Returns
a
dynamically-typed built arounda
given statically- typed allocator of type . Passinga
pointer to the allocator createsa
dynamic allocator around the allocator pointed to by the pointer, without attempting to copy or move it. Passing the allocator by value or reference behaves as follows.Discussion
- If has no state, the resulting object is allocated in static shared storage.
- If has state and is copyable, the result will store
a
copy of it within. The result itself is allocated in its own statically-typed allocator. - If has state and is not copyable, the result will move the passed-in argument into the result. The result itself is allocated in its own statically-typed allocator.
-
Declaration
class
CAllocatorImpl
(Allocator, Flag!"indirect" indirect = No.indirect): IAllocator;Implementation of
IAllocator
usingAllocator
. This adapts a statically-built allocator type toIAllocator
that is directly usable by non-templated code.Discussion
Usually
is used indirectly by calling .CAllocatorImpl
-
Declaration
ref Allocator
impl
();The implementation is available as a public member.
-
Declaration
this(Allocator*
pa
);The implementation is available as a public member.
-
Declaration
@property uint
alignment
();Returns
impl.
.alignment
-
Declaration
size_t
goodAllocSize
(size_ts
);Returns
impl.
.goodAllocSize
(s
) -
Declaration
void[]
allocate
(size_ts
, TypeInfoti
= null);Returns
impl.
.allocate
(s
) -
Declaration
void[]
alignedAllocate
(size_ts
, uinta
);If
impl.
exists, calls it and returns the result. Otherwise, always returnsalignedAllocate
null
. -
Declaration
Ternary
owns
(void[]b
);If
Allocator
implements
, forwards to it. Otherwise, returnsowns
Ternary.unknown
. -
Declaration
bool
expand
(ref void[]b
, size_ts
);Returns if defined,
false
otherwise. -
Declaration
bool
reallocate
(ref void[]b
, size_ts
);Returns .
-
Declaration
bool
alignedReallocate
(ref void[]b
, size_ts
, uinta
);Forwards to
impl.
if defined,alignedReallocate
false
otherwise. -
Declaration
bool
deallocate
(void[]b
);If
impl.
is not defined, returnsdeallocate
false
. Otherwise it forwards the call. -
Declaration
bool
deallocateAll
();Calls
impl.
and returns the result if defined, otherwise returnsdeallocateAll
()false
. -
Declaration
Ternary
empty
();Forwards to
impl.
if defined, otherwise returnsempty
()Ternary.unknown
. -
Declaration
void[]
allocateAll
();Returns
impl.
if present,allocateAll
()null
otherwise.
-
-
Declaration
class
CSharedAllocatorImpl
(Allocator, Flag!"indirect" indirect = No.indirect): ISharedAllocator;Implementation of
ISharedAllocator
usingAllocator
. This adapts a statically-built, shareable across threads, allocator type toISharedAllocator
that is directly usable by non-templated code.Discussion
Usually
is used indirectly by calling .CSharedAllocatorImpl
-
Declaration
shared ref Allocator
impl
();The implementation is available as a public member.
-
Declaration
shared this(Allocator*
pa
);The implementation is available as a public member.
-
Declaration
shared @property uint
alignment
();Returns
impl.
.alignment
-
Declaration
shared size_t
goodAllocSize
(size_ts
);Returns
impl.
.goodAllocSize
(s
) -
Declaration
shared void[]
allocate
(size_ts
, TypeInfoti
= null);Returns
impl.
.allocate
(s
) -
Declaration
shared void[]
alignedAllocate
(size_ts
, uinta
);If
impl.
exists, calls it and returns the result. Otherwise, always returnsalignedAllocate
null
. -
Declaration
shared Ternary
owns
(void[]b
);If
Allocator
implements
, forwards to it. Otherwise, returnsowns
Ternary.unknown
. -
Declaration
shared bool
expand
(ref void[]b
, size_ts
);Returns if defined,
false
otherwise. -
Declaration
shared bool
reallocate
(ref void[]b
, size_ts
);Returns .
-
Declaration
shared bool
alignedReallocate
(ref void[]b
, size_ts
, uinta
);Forwards to
impl.
if defined,alignedReallocate
false
otherwise. -
Declaration
shared bool
deallocate
(void[]b
);If
impl.
is not defined, returnsdeallocate
false
. Otherwise it forwards the call. -
Declaration
shared bool
deallocateAll
();Calls
impl.
and returns the result if defined, otherwise returnsdeallocateAll
()false
. -
Declaration
shared Ternary
empty
();Forwards to
impl.
if defined, otherwise returnsempty
()Ternary.unknown
. -
Declaration
shared void[]
allocateAll
();Returns
impl.
if present,allocateAll
()null
otherwise.
-