mirror of
https://github.com/dlang/phobos.git
synced 2025-04-30 07:00:37 +03:00
199 lines
4.5 KiB
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());
|
|
}
|