Add memory.types.Scoped
This commit is contained in:
parent
7a0241b484
commit
ba2d086fb8
@ -18,7 +18,7 @@ import std.range;
|
|||||||
import std.traits;
|
import std.traits;
|
||||||
import tanya.memory;
|
import tanya.memory;
|
||||||
|
|
||||||
package(tanya) final class RefCountedStore(T)
|
final class RefCountedStore(T)
|
||||||
{
|
{
|
||||||
T payload;
|
T payload;
|
||||||
size_t counter = 1;
|
size_t counter = 1;
|
||||||
@ -34,7 +34,7 @@ package(tanya) final class RefCountedStore(T)
|
|||||||
mixin("return " ~ op ~ "counter;");
|
mixin("return " ~ op ~ "counter;");
|
||||||
}
|
}
|
||||||
|
|
||||||
int opCmp(size_t counter)
|
int opCmp(const size_t counter)
|
||||||
{
|
{
|
||||||
if (this.counter > counter)
|
if (this.counter > counter)
|
||||||
{
|
{
|
||||||
@ -50,7 +50,7 @@ package(tanya) final class RefCountedStore(T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int opEquals(size_t counter)
|
int opEquals(const size_t counter)
|
||||||
{
|
{
|
||||||
return this.counter == counter;
|
return this.counter == counter;
|
||||||
}
|
}
|
||||||
@ -118,7 +118,12 @@ struct RefCounted(T)
|
|||||||
this(allocator);
|
this(allocator);
|
||||||
this.storage = allocator.make!Storage();
|
this.storage = allocator.make!Storage();
|
||||||
this.deleter = &separateDeleter!Payload;
|
this.deleter = &separateDeleter!Payload;
|
||||||
|
|
||||||
move(value, this.storage.payload);
|
move(value, this.storage.payload);
|
||||||
|
static if (__traits(isRef, value))
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto.
|
/// Ditto.
|
||||||
@ -139,7 +144,7 @@ struct RefCounted(T)
|
|||||||
{
|
{
|
||||||
if (count != 0)
|
if (count != 0)
|
||||||
{
|
{
|
||||||
++storage;
|
++this.storage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +157,7 @@ struct RefCounted(T)
|
|||||||
{
|
{
|
||||||
if (this.storage !is null && !(this.storage.counter && --this.storage))
|
if (this.storage !is null && !(this.storage.counter && --this.storage))
|
||||||
{
|
{
|
||||||
deleter(storage, allocator);
|
deleter(this.storage, allocator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,14 +168,16 @@ struct RefCounted(T)
|
|||||||
* If it is the last reference of the previously owned object,
|
* If it is the last reference of the previously owned object,
|
||||||
* it will be destroyed.
|
* it will be destroyed.
|
||||||
*
|
*
|
||||||
* To reset the $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null).
|
* To reset $(D_PSYMBOL RefCounted) assign $(D_KEYWORD null).
|
||||||
*
|
*
|
||||||
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
||||||
* be used. If you need a different allocator, create a new
|
* be used. If you need a different allocator, create a new
|
||||||
* $(D_PSYMBOL RefCounted) and assign it.
|
* $(D_PSYMBOL RefCounted) and assign it.
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
* rhs = $(D_KEYWORD this).
|
* rhs = New object.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD this).
|
||||||
*/
|
*/
|
||||||
ref typeof(this) opAssign()(auto ref Payload rhs)
|
ref typeof(this) opAssign()(auto ref Payload rhs)
|
||||||
{
|
{
|
||||||
@ -187,8 +194,6 @@ struct RefCounted(T)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Created with refCounted. Always destroyed togethter with the pointer.
|
|
||||||
assert(this.storage.counter != 0);
|
|
||||||
finalize(this.storage.payload);
|
finalize(this.storage.payload);
|
||||||
this.storage.payload = Payload.init;
|
this.storage.payload = Payload.init;
|
||||||
}
|
}
|
||||||
@ -206,15 +211,13 @@ struct RefCounted(T)
|
|||||||
else if (this.storage > 1)
|
else if (this.storage > 1)
|
||||||
{
|
{
|
||||||
--this.storage;
|
--this.storage;
|
||||||
this.storage = null;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Created with refCounted. Always destroyed togethter with the pointer.
|
deleter(this.storage, allocator);
|
||||||
assert(this.storage.counter != 0);
|
|
||||||
finalize(this.storage.payload);
|
|
||||||
this.storage.payload = Payload.init;
|
|
||||||
}
|
}
|
||||||
|
this.storage = null;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,6 +232,8 @@ struct RefCounted(T)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns: Reference to the owned object.
|
* Returns: Reference to the owned object.
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE cound > 0).
|
||||||
*/
|
*/
|
||||||
inout(Payload) get() inout pure nothrow @safe @nogc
|
inout(Payload) get() inout pure nothrow @safe @nogc
|
||||||
in
|
in
|
||||||
@ -240,7 +245,7 @@ struct RefCounted(T)
|
|||||||
return storage.payload;
|
return storage.payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
static if (isPointer!Payload)
|
version (D_Ddoc)
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Params:
|
* Params:
|
||||||
@ -251,6 +256,11 @@ struct RefCounted(T)
|
|||||||
*
|
*
|
||||||
* Returns: Reference to the pointed value.
|
* Returns: Reference to the pointed value.
|
||||||
*/
|
*/
|
||||||
|
ref T opUnary(string op)()
|
||||||
|
if (op == "*");
|
||||||
|
}
|
||||||
|
else static if (isPointer!Payload)
|
||||||
|
{
|
||||||
ref T opUnary(string op)()
|
ref T opUnary(string op)()
|
||||||
if (op == "*")
|
if (op == "*")
|
||||||
{
|
{
|
||||||
@ -352,6 +362,44 @@ private unittest
|
|||||||
assert(rc.count == 1);
|
assert(rc.count == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
auto rc = defaultAllocator.refCounted!int(5);
|
||||||
|
assert(rc.count == 1);
|
||||||
|
|
||||||
|
void func(RefCounted!int rc)
|
||||||
|
{
|
||||||
|
assert(rc.count == 2);
|
||||||
|
rc = null;
|
||||||
|
assert(!rc.isInitialized);
|
||||||
|
assert(rc.count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(rc.count == 1);
|
||||||
|
func(rc);
|
||||||
|
assert(rc.count == 1);
|
||||||
|
|
||||||
|
rc = null;
|
||||||
|
assert(!rc.isInitialized);
|
||||||
|
assert(rc.count == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
auto rc = defaultAllocator.refCounted!int(5);
|
||||||
|
assert(*rc == 5);
|
||||||
|
|
||||||
|
void func(RefCounted!int rc)
|
||||||
|
{
|
||||||
|
assert(rc.count == 2);
|
||||||
|
rc = defaultAllocator.refCounted!int(4);
|
||||||
|
assert(*rc == 4);
|
||||||
|
assert(rc.count == 1);
|
||||||
|
}
|
||||||
|
func(rc);
|
||||||
|
assert(*rc == 5);
|
||||||
|
}
|
||||||
|
|
||||||
private unittest
|
private unittest
|
||||||
{
|
{
|
||||||
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
|
static assert(is(typeof(RefCounted!int.storage.payload) == int*));
|
||||||
@ -420,7 +468,7 @@ body
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
||||||
* $(D_PSYMBOL RefCounted) using.
|
* $(D_PSYMBOL RefCounted).
|
||||||
*
|
*
|
||||||
* Params:
|
* Params:
|
||||||
* T = Array type.
|
* T = Array type.
|
||||||
@ -432,7 +480,8 @@ body
|
|||||||
* Precondition: $(D_INLINECODE allocator !is null
|
* Precondition: $(D_INLINECODE allocator !is null
|
||||||
* && size <= size_t.max / ElementType!T.sizeof)
|
* && size <= size_t.max / ElementType!T.sizeof)
|
||||||
*/
|
*/
|
||||||
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
|
RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size)
|
||||||
|
@trusted
|
||||||
if (isArray!T)
|
if (isArray!T)
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
@ -441,8 +490,7 @@ in
|
|||||||
}
|
}
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
auto payload = cast(T) allocator.allocate(ElementType!T.sizeof * size);
|
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||||
initializeAll(payload);
|
|
||||||
return RefCounted!T(payload, allocator);
|
return RefCounted!T(payload, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,3 +560,257 @@ private @nogc unittest
|
|||||||
}
|
}
|
||||||
assert(destroyed);
|
assert(destroyed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $(D_PSYMBOL Scoped) stores an object that gets destroyed at the end of its scope.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = Value type.
|
||||||
|
*/
|
||||||
|
struct Scoped(T)
|
||||||
|
{
|
||||||
|
static if (is(T == class) || is(T == interface) || isArray!T)
|
||||||
|
{
|
||||||
|
private alias Payload = T;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
private alias Payload = T*;
|
||||||
|
}
|
||||||
|
private Payload payload;
|
||||||
|
|
||||||
|
invariant
|
||||||
|
{
|
||||||
|
assert(payload is null || allocator_ !is null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes ownership over $(D_PARAM value), setting the counter to 1.
|
||||||
|
* $(D_PARAM value) may be a pointer, an object or a dynamic array.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* value = Value whose ownership is taken over.
|
||||||
|
* allocator = Allocator used to destroy the $(D_PARAM value) and to
|
||||||
|
* allocate/deallocate internal storage.
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE allocator !is null)
|
||||||
|
*/
|
||||||
|
this()(auto ref Payload value,
|
||||||
|
shared Allocator allocator = defaultAllocator)
|
||||||
|
{
|
||||||
|
this(allocator);
|
||||||
|
|
||||||
|
move(value, this.payload);
|
||||||
|
static if (__traits(isRef, value))
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
this(shared Allocator allocator)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
this.allocator_ = allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $(D_PSYMBOL Scoped) is noncopyable.
|
||||||
|
*/
|
||||||
|
@disable this(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the owned object.
|
||||||
|
*/
|
||||||
|
~this()
|
||||||
|
{
|
||||||
|
if (this.payload !is null)
|
||||||
|
{
|
||||||
|
allocator.dispose(this.payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialized this $(D_PARAM Scoped) and takes ownership over
|
||||||
|
* $(D_PARAM rhs).
|
||||||
|
*
|
||||||
|
* To reset $(D_PSYMBOL Scoped) assign $(D_KEYWORD null).
|
||||||
|
*
|
||||||
|
* If the allocator wasn't set before, $(D_PSYMBOL defaultAllocator) will
|
||||||
|
* be used. If you need a different allocator, create a new
|
||||||
|
* $(D_PSYMBOL Scoped) and assign it.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* rhs = New object.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD this).
|
||||||
|
*/
|
||||||
|
ref typeof(this) opAssign()(auto ref Payload rhs)
|
||||||
|
{
|
||||||
|
allocator.dispose(this.payload);
|
||||||
|
move(rhs, this.payload);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
ref typeof(this) opAssign(typeof(null))
|
||||||
|
{
|
||||||
|
allocator.dispose(this.payload);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ditto.
|
||||||
|
ref typeof(this) opAssign(typeof(this) rhs)
|
||||||
|
{
|
||||||
|
swap(this.allocator_, rhs.allocator_);
|
||||||
|
swap(this.payload, rhs.payload);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns: Reference to the owned object.
|
||||||
|
*/
|
||||||
|
inout(Payload) get() inout pure nothrow @safe @nogc
|
||||||
|
{
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
version (D_Ddoc)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Params:
|
||||||
|
* op = Operation.
|
||||||
|
*
|
||||||
|
* Dereferences the pointer. It is defined only for pointers, not for
|
||||||
|
* reference types like classes, that can be accessed directly.
|
||||||
|
*
|
||||||
|
* Returns: Reference to the pointed value.
|
||||||
|
*/
|
||||||
|
ref T opUnary(string op)()
|
||||||
|
if (op == "*");
|
||||||
|
}
|
||||||
|
else static if (isPointer!Payload)
|
||||||
|
{
|
||||||
|
ref T opUnary(string op)()
|
||||||
|
if (op == "*")
|
||||||
|
{
|
||||||
|
return *payload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin DefaultAllocator;
|
||||||
|
alias get this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc unittest
|
||||||
|
{
|
||||||
|
auto p = defaultAllocator.make!int(5);
|
||||||
|
auto s = Scoped!int(p, defaultAllocator);
|
||||||
|
assert(p is null);
|
||||||
|
assert(*s == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@nogc unittest
|
||||||
|
{
|
||||||
|
static bool destroyed = false;
|
||||||
|
|
||||||
|
struct F
|
||||||
|
{
|
||||||
|
~this() @nogc
|
||||||
|
{
|
||||||
|
destroyed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto s = Scoped!F(defaultAllocator.make!F(), defaultAllocator);
|
||||||
|
}
|
||||||
|
assert(destroyed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new object of type $(D_PARAM T) and wraps it in a
|
||||||
|
* $(D_PSYMBOL Scoped) using $(D_PARAM args) as the parameter list for
|
||||||
|
* the constructor of $(D_PARAM T).
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = Type of the constructed object.
|
||||||
|
* A = Types of the arguments to the constructor of $(D_PARAM T).
|
||||||
|
* allocator = Allocator.
|
||||||
|
* args = Constructor arguments of $(D_PARAM T).
|
||||||
|
*
|
||||||
|
* Returns: Newly created $(D_PSYMBOL Scoped!T).
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE allocator !is null)
|
||||||
|
*/
|
||||||
|
Scoped!T scoped(T, A...)(shared Allocator allocator, auto ref A args)
|
||||||
|
if (!is(T == interface) && !isAbstractClass!T
|
||||||
|
&& !isAssociativeArray!T && !isArray!T)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
auto payload = allocator.make!(T, shared Allocator, A)(args);
|
||||||
|
return Scoped!T(payload, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
|
||||||
|
* $(D_PSYMBOL Scoped).
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = Array type.
|
||||||
|
* size = Array size.
|
||||||
|
* allocator = Allocator.
|
||||||
|
*
|
||||||
|
* Returns: Newly created $(D_PSYMBOL Scoped!T).
|
||||||
|
*
|
||||||
|
* Precondition: $(D_INLINECODE allocator !is null
|
||||||
|
* && size <= size_t.max / ElementType!T.sizeof)
|
||||||
|
*/
|
||||||
|
Scoped!T scoped(T)(shared Allocator allocator, const size_t size)
|
||||||
|
@trusted
|
||||||
|
if (isArray!T)
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assert(allocator !is null);
|
||||||
|
assert(size <= size_t.max / ElementType!T.sizeof);
|
||||||
|
}
|
||||||
|
body
|
||||||
|
{
|
||||||
|
auto payload = allocator.resize!(ElementType!T)(null, size);
|
||||||
|
return Scoped!T(payload, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
static assert(is(typeof(defaultAllocator.scoped!B(5))));
|
||||||
|
static assert(is(typeof(defaultAllocator.scoped!(int[])(5))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
auto s = defaultAllocator.scoped!int(5);
|
||||||
|
assert(*s == 5);
|
||||||
|
|
||||||
|
s = null;
|
||||||
|
assert(s is null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unittest
|
||||||
|
{
|
||||||
|
auto s = defaultAllocator.scoped!int(5);
|
||||||
|
assert(*s == 5);
|
||||||
|
|
||||||
|
s = defaultAllocator.scoped!int(4);
|
||||||
|
assert(*s == 4);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user