summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/tanya/memory/smartref.d847
-rw-r--r--source/tanya/memory/types.d615
2 files changed, 877 insertions, 585 deletions
diff --git a/source/tanya/memory/smartref.d b/source/tanya/memory/smartref.d
new file mode 100644
index 0000000..8ebf399
--- /dev/null
+++ b/source/tanya/memory/smartref.d
@@ -0,0 +1,847 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Smart pointers.
+ *
+ * Copyright: Eugene Wissner 2016-2017.
+ * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
+ * Mozilla Public License, v. 2.0).
+ * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
+ */
+module tanya.memory.smartref;
+
+import core.exception;
+import std.algorithm.comparison;
+import std.algorithm.mutation;
+import std.conv;
+import std.range;
+import std.traits;
+import tanya.memory;
+
+package template Payload(T)
+{
+ static if (is(T == class) || is(T == interface) || isArray!T)
+ {
+ alias Payload = T;
+ }
+ else
+ {
+ alias Payload = T*;
+ }
+}
+
+package final class RefCountedStore(T)
+{
+ T payload;
+ size_t counter = 1;
+
+ size_t opUnary(string op)()
+ if (op == "--" || op == "++")
+ in
+ {
+ assert(counter > 0);
+ }
+ body
+ {
+ mixin("return " ~ op ~ "counter;");
+ }
+
+ int opCmp(const size_t counter)
+ {
+ if (this.counter > counter)
+ {
+ return 1;
+ }
+ else if (this.counter < counter)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ int opEquals(const size_t counter)
+ {
+ return this.counter == counter;
+ }
+}
+
+package void separateDeleter(T)(RefCountedStore!T storage,
+ shared Allocator allocator)
+{
+ allocator.dispose(storage.payload);
+ allocator.dispose(storage);
+}
+
+package void unifiedDeleter(T)(RefCountedStore!T storage,
+ shared Allocator allocator)
+{
+ auto ptr1 = finalize(storage);
+ auto ptr2 = finalize(storage.payload);
+ allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]);
+}
+
+/**
+ * Reference-counted object containing a $(D_PARAM T) value as payload.
+ * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
+ * when the reference count goes down to zero, frees the underlying store.
+ *
+ * Params:
+ * T = Type of the reference-counted value.
+ */
+struct RefCounted(T)
+{
+ private alias Storage = RefCountedStore!(Payload!T);
+
+ private Storage storage;
+ private void function(Storage storage,
+ shared Allocator allocator) @nogc deleter;
+
+ invariant
+ {
+ assert(storage is null || allocator_ !is null);
+ assert(storage is null || deleter !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!T value,
+ shared Allocator allocator = defaultAllocator)
+ {
+ this(allocator);
+ this.storage = allocator.make!Storage();
+ this.deleter = &separateDeleter!(Payload!T);
+
+ move(value, this.storage.payload);
+ static if (__traits(isRef, value))
+ {
+ value = null;
+ }
+ }
+
+ /// Ditto.
+ this(shared Allocator allocator)
+ in
+ {
+ assert(allocator !is null);
+ }
+ body
+ {
+ this.allocator_ = allocator;
+ }
+
+ /**
+ * Increases the reference counter by one.
+ */
+ this(this)
+ {
+ if (count != 0)
+ {
+ ++this.storage;
+ }
+ }
+
+ /**
+ * Decreases the reference counter by one.
+ *
+ * If the counter reaches 0, destroys the owned object.
+ */
+ ~this()
+ {
+ if (this.storage !is null && !(this.storage.counter && --this.storage))
+ {
+ deleter(this.storage, allocator);
+ }
+ }
+
+ /**
+ * Takes ownership over $(D_PARAM rhs). Initializes this
+ * $(D_PSYMBOL RefCounted) if needed.
+ *
+ * If it is the last reference of the previously owned object,
+ * it will be destroyed.
+ *
+ * To reset $(D_PSYMBOL RefCounted) 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 RefCounted) and assign it.
+ *
+ * Params:
+ * rhs = New object.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign()(auto ref Payload!T rhs)
+ {
+ if (this.storage is null)
+ {
+ this.storage = allocator.make!Storage();
+ this.deleter = &separateDeleter!(Payload!T);
+ }
+ else if (this.storage > 1)
+ {
+ --this.storage;
+ this.storage = allocator.make!Storage();
+ this.deleter = &separateDeleter!(Payload!T);
+ }
+ else
+ {
+ finalize(this.storage.payload);
+ this.storage.payload = Payload!T.init;
+ }
+ move(rhs, this.storage.payload);
+ return this;
+ }
+
+ /// Ditto.
+ ref typeof(this) opAssign(typeof(null))
+ {
+ if (this.storage is null)
+ {
+ return this;
+ }
+ else if (this.storage > 1)
+ {
+ --this.storage;
+ }
+ else
+ {
+ deleter(this.storage, allocator);
+ }
+ this.storage = null;
+
+ return this;
+ }
+
+ /// Ditto.
+ ref typeof(this) opAssign(typeof(this) rhs)
+ {
+ swap(this.allocator_, rhs.allocator_);
+ swap(this.storage, rhs.storage);
+ swap(this.deleter, rhs.deleter);
+ return this;
+ }
+
+ /**
+ * Returns: Reference to the owned object.
+ *
+ * Precondition: $(D_INLINECODE cound > 0).
+ */
+ inout(Payload!T) get() inout pure nothrow @safe @nogc
+ in
+ {
+ assert(count > 0, "Attempted to access an uninitialized reference.");
+ }
+ body
+ {
+ return storage.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 inout(T) opUnary(string op)() inout
+ if (op == "*");
+ }
+ else static if (isPointer!(Payload!T))
+ {
+ ref inout(T) opUnary(string op)() inout
+ if (op == "*")
+ {
+ return *storage.payload;
+ }
+ }
+
+ /**
+ * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
+ * storage.
+ */
+ @property bool isInitialized() const
+ {
+ return storage !is null;
+ }
+
+ /**
+ * Returns: The number of $(D_PSYMBOL RefCounted) instances that share
+ * ownership over the same pointer (including $(D_KEYWORD this)).
+ * If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`.
+ */
+ @property size_t count() const
+ {
+ return storage is null ? 0 : storage.counter;
+ }
+
+ mixin DefaultAllocator;
+ alias get this;
+}
+
+///
+unittest
+{
+ auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
+ auto val = rc.get();
+
+ *val = 8;
+ assert(*rc.storage.payload == 8);
+
+ val = null;
+ assert(rc.storage.payload !is null);
+ assert(*rc.storage.payload == 8);
+
+ *rc = 9;
+ assert(*rc.storage.payload == 9);
+}
+
+version (unittest)
+{
+ private class A
+ {
+ uint *destroyed;
+
+ this(ref uint destroyed) @nogc
+ {
+ this.destroyed = &destroyed;
+ }
+
+ ~this() @nogc
+ {
+ ++(*destroyed);
+ }
+ }
+
+ private struct B
+ {
+ int prop;
+ @disable this();
+ this(int param1) @nogc
+ {
+ prop = param1;
+ }
+ }
+}
+
+private @nogc unittest
+{
+ uint destroyed;
+ auto a = defaultAllocator.make!A(destroyed);
+
+ assert(destroyed == 0);
+ {
+ auto rc = RefCounted!A(a, defaultAllocator);
+ assert(rc.count == 1);
+
+ void func(RefCounted!A rc) @nogc
+ {
+ assert(rc.count == 2);
+ }
+ func(rc);
+
+ assert(rc.count == 1);
+ }
+ assert(destroyed == 1);
+
+ RefCounted!int rc;
+ assert(rc.count == 0);
+ rc = defaultAllocator.make!int(8);
+ assert(rc.count == 1);
+}
+
+private @nogc unittest
+{
+ auto rc = RefCounted!int(defaultAllocator);
+ assert(!rc.isInitialized);
+ assert(rc.allocator is defaultAllocator);
+}
+
+private @nogc unittest
+{
+ auto rc = defaultAllocator.refCounted!int(5);
+ assert(rc.count == 1);
+
+ void func(RefCounted!int rc) @nogc
+ {
+ 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) @nogc
+ {
+ assert(rc.count == 2);
+ rc = defaultAllocator.refCounted!int(4);
+ assert(*rc == 4);
+ assert(rc.count == 1);
+ }
+ func(rc);
+ assert(*rc == 5);
+}
+
+private unittest
+{
+ static assert(is(typeof(RefCounted!int.storage.payload) == int*));
+ static assert(is(typeof(RefCounted!A.storage.payload) == A));
+
+ static assert(is(RefCounted!B));
+ static assert(is(RefCounted!A));
+}
+
+/**
+ * Constructs a new object of type $(D_PARAM T) and wraps it in a
+ * $(D_PSYMBOL RefCounted) using $(D_PARAM args) as the parameter list for
+ * the constructor of $(D_PARAM T).
+ *
+ * This function is more efficient than the using of $(D_PSYMBOL RefCounted)
+ * directly, since it allocates only ones (the internal storage and the
+ * object).
+ *
+ * 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 RefCounted!T).
+ *
+ * Precondition: $(D_INLINECODE allocator !is null)
+ */
+RefCounted!T refCounted(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 rc = typeof(return)(allocator);
+
+ const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
+ const size = alignedSize(stateSize!T + storageSize);
+
+ auto mem = (() @trusted => allocator.allocate(size))();
+ if (mem is null)
+ {
+ onOutOfMemoryError();
+ }
+ scope (failure)
+ {
+ () @trusted { allocator.deallocate(mem); }();
+ }
+ rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]);
+
+ static if (is(T == class))
+ {
+ rc.storage.payload = emplace!T(mem[storageSize .. $], args);
+ }
+ else
+ {
+ auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
+ rc.storage.payload = emplace!T(ptr, args);
+ }
+ rc.deleter = &unifiedDeleter!(Payload!T);
+ return rc;
+}
+
+/**
+ * Constructs a new array with $(D_PARAM size) elements and wraps it in a
+ * $(D_PSYMBOL RefCounted).
+ *
+ * Params:
+ * T = Array type.
+ * size = Array size.
+ * allocator = Allocator.
+ *
+ * Returns: Newly created $(D_PSYMBOL RefCounted!T).
+ *
+ * Precondition: $(D_INLINECODE allocator !is null
+ * && size <= size_t.max / ElementType!T.sizeof)
+ */
+RefCounted!T refCounted(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 RefCounted!T(payload, allocator);
+}
+
+///
+unittest
+{
+ auto rc = defaultAllocator.refCounted!int(5);
+ assert(rc.count == 1);
+
+ void func(RefCounted!int param) @nogc
+ {
+ if (param.count == 2)
+ {
+ func(param);
+ }
+ else
+ {
+ assert(param.count == 3);
+ }
+ }
+ func(rc);
+
+ assert(rc.count == 1);
+}
+
+private @nogc unittest
+{
+ struct E
+ {
+ }
+ auto b = defaultAllocator.refCounted!B(15);
+ static assert(is(typeof(b.storage.payload) == B*));
+ static assert(is(typeof(b.prop) == int));
+ static assert(!is(typeof(defaultAllocator.refCounted!B())));
+
+ static assert(is(typeof(defaultAllocator.refCounted!E())));
+ static assert(!is(typeof(defaultAllocator.refCounted!E(5))));
+ {
+ auto rc = defaultAllocator.refCounted!B(3);
+ assert(rc.get().prop == 3);
+ }
+ {
+ auto rc = defaultAllocator.refCounted!E();
+ assert(rc.count);
+ }
+}
+
+private @nogc unittest
+{
+ auto rc = defaultAllocator.refCounted!(int[])(5);
+ assert(rc.length == 5);
+}
+
+private @nogc unittest
+{
+ auto p1 = defaultAllocator.make!int(5);
+ auto p2 = p1;
+ auto rc = RefCounted!int(p1, defaultAllocator);
+
+ assert(p1 is null);
+ assert(rc.get() is p2);
+}
+
+private @nogc unittest
+{
+ static bool destroyed = false;
+
+ struct F
+ {
+ ~this() @nogc
+ {
+ destroyed = true;
+ }
+ }
+ {
+ auto rc = defaultAllocator.refCounted!F();
+ }
+ assert(destroyed);
+}
+
+/**
+ * $(D_PSYMBOL Unique) stores an object that gets destroyed at the end of its scope.
+ *
+ * Params:
+ * T = Value type.
+ */
+struct Unique(T)
+{
+ private Payload!T 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!T 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 Unique) is noncopyable.
+ */
+ @disable this(this);
+
+ /**
+ * Destroys the owned object.
+ */
+ ~this()
+ {
+ if (this.payload !is null)
+ {
+ allocator.dispose(this.payload);
+ }
+ }
+
+ /**
+ * Initialized this $(D_PARAM Unique) and takes ownership over
+ * $(D_PARAM rhs).
+ *
+ * To reset $(D_PSYMBOL Unique) 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 Unique) and assign it.
+ *
+ * Params:
+ * rhs = New object.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign()(auto ref Payload!T 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!T) 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 inout(T) opUnary(string op)() inout
+ if (op == "*");
+ }
+ else static if (isPointer!(Payload!T))
+ {
+ ref inout(T) opUnary(string op)() inout
+ if (op == "*")
+ {
+ return *payload;
+ }
+ }
+
+ mixin DefaultAllocator;
+ alias get this;
+}
+
+///
+@nogc unittest
+{
+ auto p = defaultAllocator.make!int(5);
+ auto s = Unique!int(p, defaultAllocator);
+ assert(p is null);
+ assert(*s == 5);
+}
+
+///
+@nogc unittest
+{
+ static bool destroyed = false;
+
+ struct F
+ {
+ ~this() @nogc
+ {
+ destroyed = true;
+ }
+ }
+ {
+ auto s = Unique!F(defaultAllocator.make!F(), defaultAllocator);
+ }
+ assert(destroyed);
+}
+
+/**
+ * Constructs a new object of type $(D_PARAM T) and wraps it in a
+ * $(D_PSYMBOL Unique) 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 Unique!T).
+ *
+ * Precondition: $(D_INLINECODE allocator !is null)
+ */
+Unique!T unique(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 Unique!T(payload, allocator);
+}
+
+/**
+ * Constructs a new array with $(D_PARAM size) elements and wraps it in a
+ * $(D_PSYMBOL Unique).
+ *
+ * Params:
+ * T = Array type.
+ * size = Array size.
+ * allocator = Allocator.
+ *
+ * Returns: Newly created $(D_PSYMBOL Unique!T).
+ *
+ * Precondition: $(D_INLINECODE allocator !is null
+ * && size <= size_t.max / ElementType!T.sizeof)
+ */
+Unique!T unique(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 Unique!T(payload, allocator);
+}
+
+private unittest
+{
+ static assert(is(typeof(defaultAllocator.unique!B(5))));
+ static assert(is(typeof(defaultAllocator.unique!(int[])(5))));
+}
+
+private unittest
+{
+ auto s = defaultAllocator.unique!int(5);
+ assert(*s == 5);
+
+ s = null;
+ assert(s is null);
+}
+
+private unittest
+{
+ auto s = defaultAllocator.unique!int(5);
+ assert(*s == 5);
+
+ s = defaultAllocator.unique!int(4);
+ assert(*s == 4);
+}
+
+private @nogc unittest
+{
+ auto p1 = defaultAllocator.make!int(5);
+ auto p2 = p1;
+ auto rc = Unique!int(p1, defaultAllocator);
+
+ assert(p1 is null);
+ assert(rc.get() is p2);
+}
+
+private @nogc unittest
+{
+ auto rc = Unique!int(defaultAllocator);
+ assert(rc.allocator is defaultAllocator);
+}
diff --git a/source/tanya/memory/types.d b/source/tanya/memory/types.d
index 9a98464..e255d5a 100644
--- a/source/tanya/memory/types.d
+++ b/source/tanya/memory/types.d
@@ -3,11 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
+ * Smart pointers.
+ *
+ * $(RED Deprecated. Use $(D_PSYMBOL tanya.memory.smartref) instead.
+ * This module will be removed in 0.8.0.)
+ *
* Copyright: Eugene Wissner 2016-2017.
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
+deprecated("Use tanya.memory.smartref instead")
module tanya.memory.types;
import core.exception;
@@ -17,318 +23,10 @@ import std.conv;
import std.range;
import std.traits;
import tanya.memory;
-
-private template Payload(T)
-{
- static if (is(T == class) || is(T == interface) || isArray!T)
- {
- alias Payload = T;
- }
- else
- {
- alias Payload = T*;
- }
-}
-
-final class RefCountedStore(T)
-{
- T payload;
- size_t counter = 1;
-
- size_t opUnary(string op)()
- if (op == "--" || op == "++")
- in
- {
- assert(counter > 0);
- }
- body
- {
- mixin("return " ~ op ~ "counter;");
- }
-
- int opCmp(const size_t counter)
- {
- if (this.counter > counter)
- {
- return 1;
- }
- else if (this.counter < counter)
- {
- return -1;
- }
- else
- {
- return 0;
- }
- }
-
- int opEquals(const size_t counter)
- {
- return this.counter == counter;
- }
-}
-
-private void separateDeleter(T)(RefCountedStore!T storage,
- shared Allocator allocator)
-{
- allocator.dispose(storage.payload);
- allocator.dispose(storage);
-}
-
-private void unifiedDeleter(T)(RefCountedStore!T storage,
- shared Allocator allocator)
-{
- auto ptr1 = finalize(storage);
- auto ptr2 = finalize(storage.payload);
- allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]);
-}
-
-/**
- * Reference-counted object containing a $(D_PARAM T) value as payload.
- * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
- * when the reference count goes down to zero, frees the underlying store.
- *
- * Params:
- * T = Type of the reference-counted value.
- */
-struct RefCounted(T)
-{
- private alias Storage = RefCountedStore!(Payload!T);
-
- private Storage storage;
- private void function(Storage storage,
- shared Allocator allocator) @nogc deleter;
-
- invariant
- {
- assert(storage is null || allocator_ !is null);
- assert(storage is null || deleter !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!T value,
- shared Allocator allocator = defaultAllocator)
- {
- this(allocator);
- this.storage = allocator.make!Storage();
- this.deleter = &separateDeleter!(Payload!T);
-
- move(value, this.storage.payload);
- static if (__traits(isRef, value))
- {
- value = null;
- }
- }
-
- /// Ditto.
- this(shared Allocator allocator)
- in
- {
- assert(allocator !is null);
- }
- body
- {
- this.allocator_ = allocator;
- }
-
- /**
- * Increases the reference counter by one.
- */
- this(this)
- {
- if (count != 0)
- {
- ++this.storage;
- }
- }
-
- /**
- * Decreases the reference counter by one.
- *
- * If the counter reaches 0, destroys the owned object.
- */
- ~this()
- {
- if (this.storage !is null && !(this.storage.counter && --this.storage))
- {
- deleter(this.storage, allocator);
- }
- }
-
- /**
- * Takes ownership over $(D_PARAM rhs). Initializes this
- * $(D_PSYMBOL RefCounted) if needed.
- *
- * If it is the last reference of the previously owned object,
- * it will be destroyed.
- *
- * To reset $(D_PSYMBOL RefCounted) 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 RefCounted) and assign it.
- *
- * Params:
- * rhs = New object.
- *
- * Returns: $(D_KEYWORD this).
- */
- ref typeof(this) opAssign()(auto ref Payload!T rhs)
- {
- if (this.storage is null)
- {
- this.storage = allocator.make!Storage();
- this.deleter = &separateDeleter!(Payload!T);
- }
- else if (this.storage > 1)
- {
- --this.storage;
- this.storage = allocator.make!Storage();
- this.deleter = &separateDeleter!(Payload!T);
- }
- else
- {
- finalize(this.storage.payload);
- this.storage.payload = Payload!T.init;
- }
- move(rhs, this.storage.payload);
- return this;
- }
-
- /// Ditto.
- ref typeof(this) opAssign(typeof(null))
- {
- if (this.storage is null)
- {
- return this;
- }
- else if (this.storage > 1)
- {
- --this.storage;
- }
- else
- {
- deleter(this.storage, allocator);
- }
- this.storage = null;
-
- return this;
- }
-
- /// Ditto.
- ref typeof(this) opAssign(typeof(this) rhs)
- {
- swap(this.allocator_, rhs.allocator_);
- swap(this.storage, rhs.storage);
- swap(this.deleter, rhs.deleter);
- return this;
- }
-
- /**
- * Returns: Reference to the owned object.
- *
- * Precondition: $(D_INLINECODE cound > 0).
- */
- Payload!T get() pure nothrow @safe @nogc
- in
- {
- assert(count > 0, "Attempted to access an uninitialized reference.");
- }
- body
- {
- return storage.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!T))
- {
- ref T opUnary(string op)()
- if (op == "*")
- {
- return *storage.payload;
- }
- }
-
- /**
- * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
- * storage.
- */
- @property bool isInitialized() const
- {
- return storage !is null;
- }
-
- /**
- * Returns: The number of $(D_PSYMBOL RefCounted) instances that share
- * ownership over the same pointer (including $(D_KEYWORD this)).
- * If this $(D_PSYMBOL RefCounted) isn't initialized, returns `0`.
- */
- @property size_t count() const
- {
- return storage is null ? 0 : storage.counter;
- }
-
- mixin DefaultAllocator;
- alias get this;
-}
-
-///
-unittest
-{
- auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
- auto val = rc.get();
-
- *val = 8;
- assert(*rc.storage.payload == 8);
-
- val = null;
- assert(rc.storage.payload !is null);
- assert(*rc.storage.payload == 8);
-
- *rc = 9;
- assert(*rc.storage.payload == 9);
-}
+public import tanya.memory.smartref : RefCounted, Payload;
version (unittest)
{
- private class A
- {
- uint *destroyed;
-
- this(ref uint destroyed) @nogc
- {
- this.destroyed = &destroyed;
- }
-
- ~this() @nogc
- {
- ++(*destroyed);
- }
- }
-
private struct B
{
int prop;
@@ -340,255 +38,13 @@ version (unittest)
}
}
-private @nogc unittest
-{
- uint destroyed;
- auto a = defaultAllocator.make!A(destroyed);
-
- assert(destroyed == 0);
- {
- auto rc = RefCounted!A(a, defaultAllocator);
- assert(rc.count == 1);
-
- void func(RefCounted!A rc) @nogc
- {
- assert(rc.count == 2);
- }
- func(rc);
-
- assert(rc.count == 1);
- }
- assert(destroyed == 1);
-
- RefCounted!int rc;
- assert(rc.count == 0);
- rc = defaultAllocator.make!int(8);
- assert(rc.count == 1);
-}
-
-private @nogc unittest
-{
- auto rc = RefCounted!int(defaultAllocator);
- assert(!rc.isInitialized);
- assert(rc.allocator is defaultAllocator);
-}
-
-private @nogc unittest
-{
- auto rc = defaultAllocator.refCounted!int(5);
- assert(rc.count == 1);
-
- void func(RefCounted!int rc) @nogc
- {
- 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) @nogc
- {
- assert(rc.count == 2);
- rc = defaultAllocator.refCounted!int(4);
- assert(*rc == 4);
- assert(rc.count == 1);
- }
- func(rc);
- assert(*rc == 5);
-}
-
-private unittest
-{
- static assert(is(typeof(RefCounted!int.storage.payload) == int*));
- static assert(is(typeof(RefCounted!A.storage.payload) == A));
-
- static assert(is(RefCounted!B));
- static assert(is(RefCounted!A));
-}
-
/**
- * Constructs a new object of type $(D_PARAM T) and wraps it in a
- * $(D_PSYMBOL RefCounted) using $(D_PARAM args) as the parameter list for
- * the constructor of $(D_PARAM T).
- *
- * This function is more efficient than the using of $(D_PSYMBOL RefCounted)
- * directly, since it allocates only ones (the internal storage and the
- * object).
- *
- * 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 RefCounted!T).
- *
- * Precondition: $(D_INLINECODE allocator !is null)
- */
-RefCounted!T refCounted(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 rc = typeof(return)(allocator);
-
- const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
- const size = alignedSize(stateSize!T + storageSize);
-
- auto mem = (() @trusted => allocator.allocate(size))();
- if (mem is null)
- {
- onOutOfMemoryError();
- }
- scope (failure)
- {
- () @trusted { allocator.deallocate(mem); }();
- }
- rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]);
-
- static if (is(T == class))
- {
- rc.storage.payload = emplace!T(mem[storageSize .. $], args);
- }
- else
- {
- auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
- rc.storage.payload = emplace!T(ptr, args);
- }
- rc.deleter = &unifiedDeleter!(Payload!T);
- return rc;
-}
-
-/**
- * Constructs a new array with $(D_PARAM size) elements and wraps it in a
- * $(D_PSYMBOL RefCounted).
- *
- * Params:
- * T = Array type.
- * size = Array size.
- * allocator = Allocator.
- *
- * Returns: Newly created $(D_PSYMBOL RefCounted!T).
- *
- * Precondition: $(D_INLINECODE allocator !is null
- * && size <= size_t.max / ElementType!T.sizeof)
- */
-RefCounted!T refCounted(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 RefCounted!T(payload, allocator);
-}
-
-///
-unittest
-{
- auto rc = defaultAllocator.refCounted!int(5);
- assert(rc.count == 1);
-
- void func(RefCounted!int param) @nogc
- {
- if (param.count == 2)
- {
- func(param);
- }
- else
- {
- assert(param.count == 3);
- }
- }
- func(rc);
-
- assert(rc.count == 1);
-}
-
-private @nogc unittest
-{
- struct E
- {
- }
- auto b = defaultAllocator.refCounted!B(15);
- static assert(is(typeof(b.storage.payload) == B*));
- static assert(is(typeof(b.prop) == int));
- static assert(!is(typeof(defaultAllocator.refCounted!B())));
-
- static assert(is(typeof(defaultAllocator.refCounted!E())));
- static assert(!is(typeof(defaultAllocator.refCounted!E(5))));
- {
- auto rc = defaultAllocator.refCounted!B(3);
- assert(rc.get().prop == 3);
- }
- {
- auto rc = defaultAllocator.refCounted!E();
- assert(rc.count);
- }
-}
-
-private @nogc unittest
-{
- auto rc = defaultAllocator.refCounted!(int[])(5);
- assert(rc.length == 5);
-}
-
-private @nogc unittest
-{
- auto p1 = defaultAllocator.make!int(5);
- auto p2 = p1;
- auto rc = RefCounted!int(p1, defaultAllocator);
-
- assert(p1 is null);
- assert(rc.get() is p2);
-}
-
-private @nogc unittest
-{
- static bool destroyed = false;
-
- struct F
- {
- ~this() @nogc
- {
- destroyed = true;
- }
- }
- {
- auto rc = defaultAllocator.refCounted!F();
- }
- assert(destroyed);
-}
-
-/**
- * $(D_PSYMBOL Unique) stores an object that gets destroyed at the end of its scope.
+ * $(D_PSYMBOL Scoped) stores an object that gets destroyed at the end of its scope.
*
* Params:
* T = Value type.
*/
-struct Unique(T)
+struct Scoped(T)
{
private Payload!T payload;
@@ -632,7 +88,7 @@ struct Unique(T)
}
/**
- * $(D_PSYMBOL Unique) is noncopyable.
+ * $(D_PSYMBOL Scoped) is noncopyable.
*/
@disable this(this);
@@ -648,14 +104,14 @@ struct Unique(T)
}
/**
- * Initialized this $(D_PARAM Unique) and takes ownership over
+ * Initialized this $(D_PARAM Scoped) and takes ownership over
* $(D_PARAM rhs).
*
- * To reset $(D_PSYMBOL Unique) assign $(D_KEYWORD null).
+ * 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 Unique) and assign it.
+ * $(D_PSYMBOL Scoped) and assign it.
*
* Params:
* rhs = New object.
@@ -725,7 +181,7 @@ struct Unique(T)
@nogc unittest
{
auto p = defaultAllocator.make!int(5);
- auto s = Unique!int(p, defaultAllocator);
+ auto s = Scoped!int(p, defaultAllocator);
assert(p is null);
assert(*s == 5);
}
@@ -743,14 +199,14 @@ struct Unique(T)
}
}
{
- auto s = Unique!F(defaultAllocator.make!F(), defaultAllocator);
+ 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 Unique) using $(D_PARAM args) as the parameter list for
+ * $(D_PSYMBOL Scoped) using $(D_PARAM args) as the parameter list for
* the constructor of $(D_PARAM T).
*
* Params:
@@ -759,11 +215,11 @@ struct Unique(T)
* allocator = Allocator.
* args = Constructor arguments of $(D_PARAM T).
*
- * Returns: Newly created $(D_PSYMBOL Unique!T).
+ * Returns: Newly created $(D_PSYMBOL Scoped!T).
*
* Precondition: $(D_INLINECODE allocator !is null)
*/
-Unique!T unique(T, A...)(shared Allocator allocator, auto ref A args)
+Scoped!T scoped(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T
&& !isAssociativeArray!T && !isArray!T)
in
@@ -773,24 +229,24 @@ in
body
{
auto payload = allocator.make!(T, shared Allocator, A)(args);
- return Unique!T(payload, allocator);
+ return Scoped!T(payload, allocator);
}
/**
* Constructs a new array with $(D_PARAM size) elements and wraps it in a
- * $(D_PSYMBOL Unique).
+ * $(D_PSYMBOL Scoped).
*
* Params:
* T = Array type.
* size = Array size.
* allocator = Allocator.
*
- * Returns: Newly created $(D_PSYMBOL Unique!T).
+ * Returns: Newly created $(D_PSYMBOL Scoped!T).
*
* Precondition: $(D_INLINECODE allocator !is null
* && size <= size_t.max / ElementType!T.sizeof)
*/
-Unique!T unique(T)(shared Allocator allocator, const size_t size)
+Scoped!T scoped(T)(shared Allocator allocator, const size_t size)
@trusted
if (isArray!T)
in
@@ -801,18 +257,18 @@ in
body
{
auto payload = allocator.resize!(ElementType!T)(null, size);
- return Unique!T(payload, allocator);
+ return Scoped!T(payload, allocator);
}
private unittest
{
- static assert(is(typeof(defaultAllocator.unique!B(5))));
- static assert(is(typeof(defaultAllocator.unique!(int[])(5))));
+ static assert(is(typeof(defaultAllocator.scoped!B(5))));
+ static assert(is(typeof(defaultAllocator.scoped!(int[])(5))));
}
private unittest
{
- auto s = defaultAllocator.unique!int(5);
+ auto s = defaultAllocator.scoped!int(5);
assert(*s == 5);
s = null;
@@ -821,10 +277,10 @@ private unittest
private unittest
{
- auto s = defaultAllocator.unique!int(5);
+ auto s = defaultAllocator.scoped!int(5);
assert(*s == 5);
- s = defaultAllocator.unique!int(4);
+ s = defaultAllocator.scoped!int(4);
assert(*s == 4);
}
@@ -832,7 +288,7 @@ private @nogc unittest
{
auto p1 = defaultAllocator.make!int(5);
auto p2 = p1;
- auto rc = Unique!int(p1, defaultAllocator);
+ auto rc = Scoped!int(p1, defaultAllocator);
assert(p1 is null);
assert(rc.get() is p2);
@@ -840,17 +296,6 @@ private @nogc unittest
private @nogc unittest
{
- auto rc = Unique!int(defaultAllocator);
+ auto rc = Scoped!int(defaultAllocator);
assert(rc.allocator is defaultAllocator);
}
-
-/**
- * $(RED Deprecated. Use $(D_PSYMBOL Unique) and $(D_PSYMBOL unique) instead.
- * These aliases will be removed in 0.8.0.)
- */
-deprecated("Use Unique instead")
-alias Scoped = Unique;
-
-/// Ditto.
-deprecated("Use unique instead")
-alias scoped = unique;