phobos/std/experimental/allocator/scoped_allocator.d

199 lines
4.5 KiB
D

module std.experimental.allocator.scoped_allocator;
import std.experimental.allocator.common;
/**
$(D ScopedAllocator) delegates all allocation requests to $(D ParentAllocator).
When destroyed, the $(D ScopedAllocator) object automatically calls $(D
deallocate) for all memory allocated through its lifetime. (The $(D
deallocateAll) function is also implemented with the same semantics.)
$(D deallocate) is also supported, which is where most implementation effort
and overhead of $(D ScopedAllocator) go. If $(D deallocate) is not needed, a
simpler design combining $(D AllocatorList) with $(D Region) is recommended.
*/
struct ScopedAllocator(ParentAllocator)
{
unittest
{
testAllocator!(() => ScopedAllocator());
}
import std.experimental.allocator.affix_allocator;
import std.traits : hasMember;
private struct Node
{
Node* prev;
Node* next;
size_t length;
}
alias Allocator = AffixAllocator!(ParentAllocator, Node);
// state {
/**
If $(D ParentAllocator) is stateful, $(D parent) is a property giving access
to an $(D AffixAllocator!ParentAllocator). Otherwise, $(D parent) is an alias for $(D AffixAllocator!ParentAllocator.it).
*/
static if (stateSize!ParentAllocator)
{
Allocator parent;
}
else
{
alias parent = Allocator.it;
}
Node* root;
// }
/**
$(D ScopedAllocator) is not copyable.
*/
@disable this(this);
/**
$(D ScopedAllocator)'s destructor releases all memory allocated during its
lifetime.
*/
~this()
{
deallocateAll;
}
/// Alignment offered
enum alignment = Allocator.alignment;
/**
Forwards to $(D parent.goodAllocSize) (which accounts for the management
overhead).
*/
size_t goodAllocSize(size_t n)
{
return parent.goodAllocSize(n);
}
/**
Allocates memory. For management it actually allocates extra memory from
the parent.
*/
void[] allocate(size_t n)
{
auto b = parent.allocate(n);
if (!b.ptr) return b;
Node* toInsert = & parent.prefix(b);
toInsert.prev = null;
toInsert.next = root;
toInsert.length = n;
root = toInsert;
return b;
}
/**
Forwards to $(D parent.expand(b, delta)).
*/
static if (hasMember!(Allocator, "expand"))
bool expand(ref void[] b, size_t delta)
{
auto result = parent.expand(b, delta);
if (result && b.ptr)
{
parent.prefix(b).length = b.length;
}
return result;
}
/**
Reallocates $(D b) to new size $(D s).
*/
bool reallocate(ref void[] b, size_t s)
{
// Remove from list
if (b.ptr)
{
Node* n = & parent.prefix(b);
if (n.prev) n.prev.next = n.next;
else root = n.next;
if (n.next) n.next.prev = n.prev;
}
auto result = parent.reallocate(b, s);
// Add back to list
if (b.ptr)
{
Node* n = & parent.prefix(b);
n.prev = null;
n.next = root;
n.length = s;
root = n;
}
return result;
}
/**
Forwards to $(D parent.owns(b)).
*/
static if (hasMember!(Allocator, "owns"))
bool owns(void[] b)
{
return parent.owns(b);
}
/**
Deallocates $(D b).
*/
static if (hasMember!(Allocator, "deallocate"))
void deallocate(void[] b)
{
// Remove from list
if (b.ptr)
{
Node* n = & parent.prefix(b);
if (n.prev) n.prev.next = n.next;
else root = n.next;
if (n.next) n.next.prev = n.prev;
}
parent.deallocate(b);
}
/**
Deallocates all memory allocated.
*/
void deallocateAll()
{
for (auto n = root; n; )
{
void* p = n + 1;
auto length = n.length;
n = n.next;
parent.deallocate(p[0 .. length]);
}
root = null;
}
/**
Returns $(D true) if this allocator is not responsible for any memory.
*/
bool empty() const
{
return root is null;
}
}
///
unittest
{
import std.experimental.allocator.mallocator;
ScopedAllocator!Mallocator alloc;
assert(alloc.empty);
auto b = alloc.allocate(10);
assert(b.length == 10);
assert(!alloc.empty);
}
unittest
{
import std.experimental.allocator.gc_allocator;
testAllocator!(() => ScopedAllocator!GCAllocator());
}