mirror of
https://github.com/dlang/phobos.git
synced 2025-05-01 23:50:31 +03:00
289 lines
9.7 KiB
D
289 lines
9.7 KiB
D
module std.experimental.allocator.affix_allocator;
|
|
|
|
/**
|
|
|
|
Allocator that adds some extra data before (of type $(D Prefix)) and/or after
|
|
(of type $(D Suffix)) any allocation made with its parent allocator. This is
|
|
useful for uses where additional allocation-related information is needed, such
|
|
as mutexes, reference counts, or walls for debugging memory corruption errors.
|
|
|
|
If $(D Prefix) is not $(D void), $(D Allocator) must guarantee an alignment at
|
|
least as large as $(D Prefix.alignof).
|
|
|
|
Suffixes are slower to get at because of alignment rounding, so prefixes should
|
|
be preferred. However, small prefixes blunt the alignment so if a large
|
|
alignment with a small affix is needed, suffixes should be chosen.
|
|
|
|
The following methods are defined if $(D Allocator) defines them, and forward to it: $(D deallocateAll), $(D empty), $(D owns).
|
|
*/
|
|
struct AffixAllocator(Allocator, Prefix, Suffix = void)
|
|
{
|
|
import std.conv, std.experimental.allocator.common, std.traits;
|
|
import std.algorithm : min;
|
|
|
|
static assert(
|
|
!stateSize!Prefix || Allocator.alignment >= Prefix.alignof,
|
|
"AffixAllocator does not work with allocators offering a smaller"
|
|
~ " alignment than the prefix alignment.");
|
|
static assert(alignment % Suffix.alignof == 0,
|
|
"This restriction could be relaxed in the future.");
|
|
|
|
/**
|
|
If $(D Prefix) is $(D void), the alignment is that of the parent. Otherwise, the alignment is the same as the $(D Prefix)'s alignment.
|
|
*/
|
|
enum uint alignment = isPowerOf2(stateSize!Prefix)
|
|
? min(stateSize!Prefix, Allocator.alignment)
|
|
: (stateSize!Prefix ? Prefix.alignof : Allocator.alignment);
|
|
|
|
/**
|
|
If the parent allocator $(D Allocator) is stateful, an instance of it is
|
|
stored as a member. Otherwise, $(D AffixAllocator) uses $(D Allocator.it).
|
|
In either case, the name $(D _parent) is uniformly used for accessing the
|
|
parent allocator.
|
|
*/
|
|
static if (stateSize!Allocator) Allocator parent;
|
|
else alias parent = Allocator.it;
|
|
|
|
template Impl()
|
|
{
|
|
size_t goodAllocSize(size_t s)
|
|
{
|
|
auto a = actualAllocationSize(s);
|
|
return roundUpToMultipleOf(parent.goodAllocSize(a)
|
|
- stateSize!Prefix - stateSize!Suffix,
|
|
this.alignment);
|
|
}
|
|
|
|
private size_t actualAllocationSize(size_t s) const
|
|
{
|
|
assert(s > 0);
|
|
static if (!stateSize!Suffix)
|
|
{
|
|
return s + stateSize!Prefix;
|
|
}
|
|
else
|
|
{
|
|
return
|
|
roundUpToMultipleOf(s + stateSize!Prefix, Suffix.alignof)
|
|
+ stateSize!Suffix;
|
|
}
|
|
}
|
|
|
|
private void[] actualAllocation(void[] b) const
|
|
{
|
|
assert(b !is null);
|
|
return (b.ptr - stateSize!Prefix)
|
|
[0 .. actualAllocationSize(b.length)];
|
|
}
|
|
|
|
void[] allocate(size_t bytes)
|
|
{
|
|
if (!bytes) return null;
|
|
auto result = parent.allocate(actualAllocationSize(bytes));
|
|
if (result is null) return null;
|
|
static if (stateSize!Prefix)
|
|
{
|
|
assert(result.ptr.alignedAt(Prefix.alignof));
|
|
emplace!Prefix(cast(Prefix*)result.ptr);
|
|
}
|
|
static if (stateSize!Suffix)
|
|
{
|
|
auto suffixP = result.ptr + result.length - Suffix.sizeof;
|
|
assert(suffixP.alignedAt(Suffix.alignof));
|
|
emplace!Suffix(cast(Suffix*)(suffixP));
|
|
}
|
|
return result[stateSize!Prefix .. stateSize!Prefix + bytes];
|
|
}
|
|
|
|
static if (hasMember!(Allocator, "allocateAll"))
|
|
void[] allocateAll()
|
|
{
|
|
auto result = parent.allocateAll();
|
|
if (result is null) return null;
|
|
if (result.length < actualAllocationSize(1))
|
|
{
|
|
deallocate(result);
|
|
return null;
|
|
}
|
|
static if (stateSize!Prefix)
|
|
{
|
|
assert(result.length > stateSize!Prefix);
|
|
emplace!Prefix(cast(Prefix*)result.ptr);
|
|
result = result[stateSize!Prefix .. $];
|
|
}
|
|
static if (stateSize!Suffix)
|
|
{
|
|
assert(result.length > stateSize!Suffix);
|
|
// Ehm, find a properly aligned place for the suffix
|
|
auto p = (result.ptr + result.length - stateSize!Suffix)
|
|
.alignDownTo(Suffix.alignof);
|
|
assert(p > result.ptr);
|
|
emplace!Suffix(cast(Suffix*) p);
|
|
result = result[0 .. p - result.ptr];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static if (hasMember!(Allocator, "owns"))
|
|
bool owns(void[] b)
|
|
{
|
|
return b !is null && parent.owns(actualAllocation(b));
|
|
}
|
|
|
|
static if (hasMember!(Allocator, "resolveInternalPointer"))
|
|
void[] resolveInternalPointer(void* p)
|
|
{
|
|
auto p1 = parent.resolveInternalPointer(p);
|
|
if (p1 is null) return p1;
|
|
p1 = p1[stateSize!Prefix .. $];
|
|
auto p2 = (p1.ptr + p1.length - stateSize!Suffix)
|
|
.alignDownTo(Suffix.alignof);
|
|
return p1[0 .. p2 - p1.ptr];
|
|
}
|
|
|
|
static if (!stateSize!Suffix && hasMember!(Allocator, "expand"))
|
|
bool expand(ref void[] b, size_t delta)
|
|
{
|
|
if (!b.ptr)
|
|
{
|
|
b = allocate(delta);
|
|
return b.length == delta;
|
|
}
|
|
auto t = actualAllocation(b);
|
|
auto result = parent.expand(t, delta);
|
|
if (!result) return false;
|
|
b = b.ptr[0 .. b.length + delta];
|
|
return true;
|
|
}
|
|
|
|
static if (hasMember!(Allocator, "reallocate"))
|
|
bool reallocate(ref void[] b, size_t s)
|
|
{
|
|
if (b is null)
|
|
{
|
|
b = allocate(s);
|
|
return b.length == s;
|
|
}
|
|
auto t = actualAllocation(b);
|
|
auto result = parent.reallocate(t, actualAllocationSize(s));
|
|
if (!result) return false; // no harm done
|
|
b = t.ptr[stateSize!Prefix .. stateSize!Prefix + s];
|
|
return true;
|
|
}
|
|
|
|
static if (hasMember!(Allocator, "deallocate"))
|
|
void deallocate(void[] b)
|
|
{
|
|
if (b.ptr) parent.deallocate(actualAllocation(b));
|
|
}
|
|
|
|
/* The following methods are defined if $(D ParentAllocator) defines
|
|
them, and forward to it: $(D deallocateAll), $(D empty).*/
|
|
mixin(forwardToMember("parent",
|
|
"deallocateAll", "empty"));
|
|
|
|
// Extra functions
|
|
static if (stateSize!Prefix)
|
|
static ref Prefix prefix(void[] b)
|
|
{
|
|
assert(b.ptr && b.ptr.alignedAt(Prefix.alignof));
|
|
return (cast(Prefix*)b.ptr)[-1];
|
|
}
|
|
static if (stateSize!Suffix)
|
|
ref Suffix suffix(void[] b)
|
|
{
|
|
assert(b.ptr);
|
|
auto p = b.ptr - stateSize!Prefix
|
|
+ actualAllocationSize(b.length);
|
|
assert(p && p.alignedAt(Suffix.alignof));
|
|
return (cast(Suffix*) p)[-1];
|
|
}
|
|
}
|
|
|
|
version (StdDdoc)
|
|
{
|
|
/**
|
|
Standard allocator methods. Each is defined if and only if the parent
|
|
allocator defines the homonym method (except for $(D goodAllocSize),
|
|
which may use the global default). Also, the methods will be $(D
|
|
shared) if the parent allocator defines them as such.
|
|
*/
|
|
size_t goodAllocSize(size_t);
|
|
/// Ditto
|
|
void[] allocate(size_t);
|
|
/// Ditto
|
|
bool owns(void[]);
|
|
/// Ditto
|
|
bool expand(ref void[] b, size_t delta);
|
|
/// Ditto
|
|
bool reallocate(ref void[] b, size_t s);
|
|
/// Ditto
|
|
void deallocate(void[] b);
|
|
/// Ditto
|
|
void deallocateAll();
|
|
/// Ditto
|
|
bool empty();
|
|
|
|
/**
|
|
The $(D it) singleton is defined if and only if the parent allocator has no state and defines its own $(D it) object.
|
|
*/
|
|
static AffixAllocator it;
|
|
|
|
/**
|
|
Affix access functions offering mutable references to the affixes of a block previously allocated with this allocator. $(D b) may not be null. They are defined if and only if the corresponding affix is not $(D void).
|
|
|
|
Precondition: $(D b !is null)
|
|
*/
|
|
static ref Prefix prefix(void[] b);
|
|
/// Ditto
|
|
static ref Suffix suffix(void[] b);
|
|
}
|
|
else static if (is(typeof(Allocator.it) == shared))
|
|
{
|
|
static shared AffixAllocator it;
|
|
shared { mixin Impl!(); }
|
|
}
|
|
else
|
|
{
|
|
mixin Impl!();
|
|
static if (stateSize!Allocator == 0)
|
|
static __gshared AffixAllocator it;
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.experimental.allocator.mallocator;
|
|
// One word before and after each allocation.
|
|
alias A = AffixAllocator!(Mallocator, size_t, size_t);
|
|
auto b = A.it.allocate(11);
|
|
A.it.prefix(b) = 0xCAFE_BABE;
|
|
A.it.suffix(b) = 0xDEAD_BEEF;
|
|
assert(A.it.prefix(b) == 0xCAFE_BABE && A.it.suffix(b) == 0xDEAD_BEEF);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.experimental.allocator.heap_block;
|
|
import std.experimental.allocator.common;
|
|
testAllocator!({
|
|
auto a = AffixAllocator!(HeapBlock!128, ulong, ulong)
|
|
(HeapBlock!128(new void[128 * 4096]));
|
|
return a;
|
|
});
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.experimental.allocator.mallocator;
|
|
alias A = AffixAllocator!(Mallocator, size_t);
|
|
auto b = A.it.allocate(10);
|
|
A.it.prefix(b) = 10;
|
|
assert(A.it.prefix(b) == 10);
|
|
|
|
import std.experimental.allocator.null_allocator;
|
|
alias B = AffixAllocator!(NullAllocator, size_t);
|
|
b = B.it.allocate(100);
|
|
assert(b is null);
|
|
}
|