summaryrefslogtreecommitdiff
path: root/middle
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2019-03-18 13:03:52 +0100
committerEugen Wissner <belka@caraus.de>2019-03-19 07:45:52 +0100
commit484cb13317921c78468dc3f9c16a0fc786b119c0 (patch)
tree22abd11091872e470112426a97ec9a89809a8e3c /middle
parent5ab99cf8873b130284336c52551ccede28f6cb2e (diff)
downloadtanya-484cb13317921c78468dc3f9c16a0fc786b119c0.tar.gz
Separate non-documentation tests from the code
Diffstat (limited to 'middle')
-rw-r--r--middle/dub.json22
-rw-r--r--middle/tanya/memory/allocator.d81
-rw-r--r--middle/tanya/memory/lifetime.d815
-rw-r--r--middle/tanya/memory/mallocator.d218
-rw-r--r--middle/tanya/memory/mmappool.d503
-rw-r--r--middle/tanya/memory/op.d375
-rw-r--r--middle/tanya/memory/package.d132
-rw-r--r--middle/tanya/memory/smartref.d634
8 files changed, 2780 insertions, 0 deletions
diff --git a/middle/dub.json b/middle/dub.json
new file mode 100644
index 0000000..eecf1d4
--- /dev/null
+++ b/middle/dub.json
@@ -0,0 +1,22 @@
+{
+ "name": "middle",
+ "description": "Runtime, middle-level utilities",
+ "targetType": "library",
+
+ "dependencies": {
+ "tanya:meta": "*",
+ "tanya:os": "*",
+ "tanya:sys": "*"
+ },
+
+ "dependencies-linux": {
+ "mir-linux-kernel": "~>1.0.0"
+ },
+
+ "sourcePaths": [
+ "."
+ ],
+ "importPaths": [
+ "."
+ ]
+}
diff --git a/middle/tanya/memory/allocator.d b/middle/tanya/memory/allocator.d
new file mode 100644
index 0000000..bf356af
--- /dev/null
+++ b/middle/tanya/memory/allocator.d
@@ -0,0 +1,81 @@
+/* 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/. */
+
+/**
+ * This module contains the interface for implementing custom allocators.
+ *
+ * Allocators are classes encapsulating memory allocation strategy. This allows
+ * to decouple memory management from the algorithms and the data.
+ *
+ * Copyright: Eugene Wissner 2016-2019.
+ * 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)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/allocator.d,
+ * tanya/memory/allocator.d)
+ */
+module tanya.memory.allocator;
+
+/**
+ * Abstract class implementing a basic allocator.
+ */
+interface Allocator
+{
+ /**
+ * Returns: Alignment offered.
+ */
+ @property uint alignment() const shared pure nothrow @safe @nogc;
+
+ /**
+ * Allocates $(D_PARAM size) bytes of memory.
+ *
+ * Params:
+ * size = Amount of memory to allocate.
+ *
+ * Returns: Pointer to the new allocated memory.
+ */
+ void[] allocate(size_t size) shared pure nothrow @nogc;
+
+ /**
+ * Deallocates a memory block.
+ *
+ * Params:
+ * p = A pointer to the memory block to be freed.
+ *
+ * Returns: Whether the deallocation was successful.
+ */
+ bool deallocate(void[] p) shared pure nothrow @nogc;
+
+ /**
+ * Increases or decreases the size of a memory block.
+ *
+ * Params:
+ * p = A pointer to the memory block.
+ * size = Size of the reallocated block.
+ *
+ * Returns: Pointer to the allocated memory.
+ */
+ bool reallocate(ref void[] p, size_t size) shared pure nothrow @nogc;
+
+ /**
+ * Reallocates a memory block in place if possible or returns
+ * $(D_KEYWORD false). This function cannot be used to allocate or
+ * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
+ * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false).
+ *
+ * Params:
+ * p = A pointer to the memory block.
+ * size = Size of the reallocated block.
+ *
+ * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
+ */
+ bool reallocateInPlace(ref void[] p, size_t size)
+ shared pure nothrow @nogc;
+}
+
+package template GetPureInstance(T : Allocator)
+{
+ alias GetPureInstance = shared(T) function()
+ pure nothrow @nogc;
+}
diff --git a/middle/tanya/memory/lifetime.d b/middle/tanya/memory/lifetime.d
new file mode 100644
index 0000000..9bdc786
--- /dev/null
+++ b/middle/tanya/memory/lifetime.d
@@ -0,0 +1,815 @@
+/* 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/. */
+
+/**
+ * Lifetime management functions, types and related exceptions.
+ *
+ * Copyright: Eugene Wissner 2019.
+ * 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)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/lifetime.d,
+ * tanya/memory/lifetime.d)
+ */
+module tanya.memory.lifetime;
+
+import tanya.memory : defaultAllocator;
+import tanya.memory.allocator;
+import tanya.meta.metafunction;
+import tanya.meta.trait;
+
+/**
+ * Error thrown if memory allocation fails.
+ */
+final class OutOfMemoryError : Error
+{
+ /**
+ * Constructs new error.
+ *
+ * Params:
+ * msg = The message for the exception.
+ * file = The file where the exception occurred.
+ * line = The line number where the exception occurred.
+ * next = The previous exception in the chain of exceptions, if any.
+ */
+ this(string msg = "Out of memory",
+ string file = __FILE__,
+ size_t line = __LINE__,
+ Throwable next = null) @nogc nothrow pure @safe
+ {
+ super(msg, file, line, next);
+ }
+
+ /// ditto
+ this(string msg,
+ Throwable next,
+ string file = __FILE__,
+ size_t line = __LINE__) @nogc nothrow pure @safe
+ {
+ super(msg, file, line, next);
+ }
+}
+
+/**
+ * Allocates $(D_PSYMBOL OutOfMemoryError) in a static storage and throws it.
+ *
+ * Params:
+ * msg = Custom error message.
+ *
+ * Throws: $(D_PSYMBOL OutOfMemoryError).
+ */
+void onOutOfMemoryError(string msg = "Out of memory")
+@nogc nothrow pure @trusted
+{
+ static ubyte[stateSize!OutOfMemoryError] memory;
+ alias PureType = OutOfMemoryError function(string) @nogc nothrow pure;
+ throw (cast(PureType) () => emplace!OutOfMemoryError(memory))(msg);
+}
+
+// From druntime
+extern (C)
+private void _d_monitordelete(Object h, bool det) @nogc nothrow pure;
+
+/*
+ * Internal function used to create, resize or destroy a dynamic array. It
+ * may throw $(D_PSYMBOL OutOfMemoryError). The new
+ * allocated part of the array isn't initialized. This function can be trusted
+ * only in the data structures that can ensure that the array is
+ * allocated/rellocated/deallocated with the same allocator.
+ *
+ * Params:
+ * T = Element type of the array being created.
+ * allocator = The allocator used for getting memory.
+ * array = A reference to the array being changed.
+ * length = New array length.
+ *
+ * Returns: $(D_PARAM array).
+ */
+package(tanya) T[] resize(T)(shared Allocator allocator,
+ auto ref T[] array,
+ const size_t length) @trusted
+{
+ if (length == 0)
+ {
+ if (allocator.deallocate(array))
+ {
+ return null;
+ }
+ else
+ {
+ onOutOfMemoryError();
+ }
+ }
+
+ void[] buf = array;
+ if (!allocator.reallocate(buf, length * T.sizeof))
+ {
+ onOutOfMemoryError();
+ }
+ // Casting from void[] is unsafe, but we know we cast to the original type.
+ array = cast(T[]) buf;
+
+ return array;
+}
+
+/*
+ * Destroys the object.
+ * Returns the memory should be freed.
+ */
+package(tanya.memory) void[] finalize(T)(ref T* p)
+{
+ if (p is null)
+ {
+ return null;
+ }
+ static if (hasElaborateDestructor!T)
+ {
+ destroy(*p);
+ }
+ return (cast(void*) p)[0 .. T.sizeof];
+}
+
+package(tanya.memory) void[] finalize(T)(ref T p)
+if (isPolymorphicType!T)
+{
+ if (p is null)
+ {
+ return null;
+ }
+ static if (is(T == interface))
+ {
+ version(Windows)
+ {
+ import core.sys.windows.unknwn : IUnknown;
+ static assert(!is(T : IUnknown), "COM interfaces can't be destroyed in "
+ ~ __PRETTY_FUNCTION__);
+ }
+ auto ob = cast(Object) p;
+ }
+ else
+ {
+ alias ob = p;
+ }
+ auto ptr = cast(void*) ob;
+ auto support = ptr[0 .. typeid(ob).initializer.length];
+
+ auto ppv = cast(void**) ptr;
+ if (!*ppv)
+ {
+ return null;
+ }
+ auto pc = cast(ClassInfo*) *ppv;
+ scope (exit)
+ {
+ *ppv = null;
+ }
+
+ auto c = *pc;
+ do
+ {
+ // Assume the destructor is @nogc. Leave it nothrow since the destructor
+ // shouldn't throw and if it does, it is an error anyway.
+ if (c.destructor)
+ {
+ alias DtorType = void function(Object) pure nothrow @safe @nogc;
+ (cast(DtorType) c.destructor)(ob);
+ }
+ }
+ while ((c = c.base) !is null);
+
+ if (ppv[1]) // if monitor is not null
+ {
+ _d_monitordelete(cast(Object) ptr, true);
+ }
+ return support;
+}
+
+package(tanya.memory) void[] finalize(T)(ref T[] p)
+{
+ destroyAllImpl!(T[], T)(p);
+ return p;
+}
+
+package(tanya) void destroyAllImpl(R, E)(R p)
+{
+ static if (hasElaborateDestructor!E)
+ {
+ foreach (ref e; p)
+ {
+ destroy(e);
+ }
+ }
+}
+
+/**
+ * Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T).
+ * It is assumed the respective entities had been allocated with the same
+ * allocator.
+ *
+ * Params:
+ * T = Type of $(D_PARAM p).
+ * allocator = Allocator the $(D_PARAM p) was allocated with.
+ * p = Object or array to be destroyed.
+ */
+void dispose(T)(shared Allocator allocator, auto ref T p)
+{
+ () @trusted { allocator.deallocate(finalize(p)); }();
+ p = null;
+}
+
+/**
+ * Constructs a new class instance of type $(D_PARAM T) using $(D_PARAM args)
+ * as the parameter list for the constructor of $(D_PARAM T).
+ *
+ * Params:
+ * T = Class type.
+ * 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 T).
+ *
+ * Precondition: $(D_INLINECODE allocator !is null)
+ */
+T make(T, A...)(shared Allocator allocator, auto ref A args)
+if (is(T == class))
+in (allocator !is null)
+{
+ auto mem = (() @trusted => allocator.allocate(stateSize!T))();
+ if (mem is null)
+ {
+ onOutOfMemoryError();
+ }
+ scope (failure)
+ {
+ () @trusted { allocator.deallocate(mem); }();
+ }
+
+ return emplace!T(mem[0 .. stateSize!T], args);
+}
+
+/**
+ * Constructs a value object of type $(D_PARAM T) using $(D_PARAM args)
+ * as the parameter list for the constructor of $(D_PARAM T) and returns a
+ * pointer to the new object.
+ *
+ * Params:
+ * T = Object type.
+ * A = Types of the arguments to the constructor of $(D_PARAM T).
+ * allocator = Allocator.
+ * args = Constructor arguments of $(D_PARAM T).
+ *
+ * Returns: Pointer to the created object.
+ *
+ * Precondition: $(D_INLINECODE allocator !is null)
+ */
+T* make(T, A...)(shared Allocator allocator, auto ref A args)
+if (!isPolymorphicType!T && !isAssociativeArray!T && !isArray!T)
+in (allocator !is null)
+{
+ auto mem = (() @trusted => allocator.allocate(stateSize!T))();
+ if (mem is null)
+ {
+ onOutOfMemoryError();
+ }
+ scope (failure)
+ {
+ () @trusted { allocator.deallocate(mem); }();
+ }
+ return emplace!T(mem[0 .. stateSize!T], args);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int* i = defaultAllocator.make!int(5);
+ assert(*i == 5);
+ defaultAllocator.dispose(i);
+}
+
+/**
+ * Constructs a new array with $(D_PARAM n) elements.
+ *
+ * Params:
+ * T = Array type.
+ * E = Array element type.
+ * allocator = Allocator.
+ * n = Array size.
+ *
+ * Returns: Newly created array.
+ *
+ * Precondition: $(D_INLINECODE allocator !is null
+ * && n <= size_t.max / E.sizeof)
+ */
+T make(T : E[], E)(shared Allocator allocator, size_t n)
+in (allocator !is null)
+in (n <= size_t.max / E.sizeof)
+{
+ auto ret = allocator.resize!E(null, n);
+
+ static if (hasElaborateDestructor!E)
+ {
+ for (auto range = ret; range.length != 0; range = range[1 .. $])
+ {
+ emplace!E(cast(void[]) range[0 .. 1], E.init);
+ }
+ }
+ else
+ {
+ ret[] = E.init;
+ }
+
+ return ret;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int[] i = defaultAllocator.make!(int[])(2);
+ assert(i.length == 2);
+ assert(i[0] == int.init && i[1] == int.init);
+ defaultAllocator.dispose(i);
+}
+
+/**
+ * Constructs a new object of type $(D_PARAM T) in $(D_PARAM memory) with the
+ * given arguments.
+ *
+ * If $(D_PARAM T) is a $(D_KEYWORD class), emplace returns a class reference
+ * of type $(D_PARAM T), otherwise a pointer to the constructed object is
+ * returned.
+ *
+ * If $(D_PARAM T) is a nested class inside another class, $(D_PARAM outer)
+ * should be an instance of the outer class.
+ *
+ * $(D_PARAM args) are arguments for the constructor of $(D_PARAM T). If
+ * $(D_PARAM T) isn't an aggregate type and doesn't have a constructor,
+ * $(D_PARAM memory) can be initialized to `args[0]` if `Args.length == 1`,
+ * `Args[0]` should be implicitly convertible to $(D_PARAM T) then.
+ *
+ * Params:
+ * T = Constructed type.
+ * U = Type of the outer class if $(D_PARAM T) is a nested class.
+ * Args = Types of the constructor arguments if $(D_PARAM T) has a constructor
+ * or the type of the initial value.
+ * outer = Outer class instance if $(D_PARAM T) is a nested class.
+ * args = Constructor arguments if $(D_PARAM T) has a constructor or the
+ * initial value.
+ *
+ * Returns: New instance of type $(D_PARAM T) constructed in $(D_PARAM memory).
+ *
+ * Precondition: `memory.length == stateSize!T`.
+ * Postcondition: $(D_PARAM memory) and the result point to the same memory.
+ */
+T emplace(T, U, Args...)(void[] memory, U outer, auto ref Args args)
+if (!isAbstractClass!T && isInnerClass!T && is(typeof(T.outer) == U))
+in (memory.length >= stateSize!T)
+out (result; memory.ptr is (() @trusted => cast(void*) result)())
+{
+ import tanya.memory.op : copy;
+
+ copy(typeid(T).initializer, memory);
+
+ auto result = (() @trusted => cast(T) memory.ptr)();
+ result.outer = outer;
+
+ static if (is(typeof(result.__ctor(args))))
+ {
+ result.__ctor(args);
+ }
+
+ return result;
+}
+
+/// ditto
+T emplace(T, Args...)(void[] memory, auto ref Args args)
+if (is(T == class) && !isAbstractClass!T && !isInnerClass!T)
+in (memory.length == stateSize!T)
+out (result; memory.ptr is (() @trusted => cast(void*) result)())
+{
+ import tanya.memory.op : copy;
+
+ copy(typeid(T).initializer, memory);
+
+ auto result = (() @trusted => cast(T) memory.ptr)();
+ static if (is(typeof(result.__ctor(args))))
+ {
+ result.__ctor(args);
+ }
+ return result;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ class C
+ {
+ int i = 5;
+ class Inner
+ {
+ int i;
+
+ this(int param) pure nothrow @safe @nogc
+ {
+ this.i = param;
+ }
+ }
+ }
+ ubyte[stateSize!C] memory1;
+ ubyte[stateSize!(C.Inner)] memory2;
+
+ auto c = emplace!C(memory1);
+ assert(c.i == 5);
+
+ auto inner = emplace!(C.Inner)(memory2, c, 8);
+ assert(c.i == 5);
+ assert(inner.i == 8);
+ assert(inner.outer is c);
+}
+
+/// ditto
+T* emplace(T, Args...)(void[] memory, auto ref Args args)
+if (!isAggregateType!T && (Args.length <= 1))
+in (memory.length >= T.sizeof)
+out (result; memory.ptr is result)
+{
+ auto result = (() @trusted => cast(T*) memory.ptr)();
+ static if (Args.length == 1)
+ {
+ *result = T(args[0]);
+ }
+ else
+ {
+ *result = T.init;
+ }
+ return result;
+}
+
+private void initializeOne(T)(ref void[] memory, ref T* result) @trusted
+{
+ import tanya.memory.op : copy, fill;
+
+ static if (!hasElaborateAssign!T && isAssignable!T)
+ {
+ *result = T.init;
+ }
+ else static if (__VERSION__ >= 2083 // __traits(isZeroInit) available.
+ && __traits(isZeroInit, T))
+ {
+ memory.ptr[0 .. T.sizeof].fill!0;
+ }
+ else
+ {
+ static immutable T init = T.init;
+ copy((&init)[0 .. 1], memory);
+ }
+}
+
+/// ditto
+T* emplace(T, Args...)(void[] memory, auto ref Args args)
+if (!isPolymorphicType!T && isAggregateType!T)
+in (memory.length >= T.sizeof)
+out (result; memory.ptr is result)
+{
+ auto result = (() @trusted => cast(T*) memory.ptr)();
+
+ static if (Args.length == 0)
+ {
+ static assert(is(typeof({ static T t; })),
+ "Default constructor is disabled");
+ initializeOne(memory, result);
+ }
+ else static if (is(typeof(result.__ctor(args))))
+ {
+ initializeOne(memory, result);
+ result.__ctor(args);
+ }
+ else static if (Args.length == 1 && is(typeof({ T t = args[0]; })))
+ {
+ import tanya.memory.op : copy;
+
+ ((ref arg) @trusted =>
+ copy((cast(void*) &arg)[0 .. T.sizeof], memory))(args[0]);
+ static if (hasElaborateCopyConstructor!T)
+ {
+ result.__postblit();
+ }
+ }
+ else static if (is(typeof({ T t = T(args); })))
+ {
+ auto init = T(args);
+ (() @trusted => moveEmplace(init, *result))();
+ }
+ else
+ {
+ static assert(false,
+ "Unable to construct value with the given arguments");
+ }
+ return result;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ ubyte[4] memory;
+
+ auto i = emplace!int(memory);
+ static assert(is(typeof(i) == int*));
+ assert(*i == 0);
+
+ i = emplace!int(memory, 5);
+ assert(*i == 5);
+
+ static struct S
+ {
+ int i;
+ @disable this();
+ @disable this(this);
+ this(int i) @nogc nothrow pure @safe
+ {
+ this.i = i;
+ }
+ }
+ auto s = emplace!S(memory, 8);
+ static assert(is(typeof(s) == S*));
+ assert(s.i == 8);
+}
+
+private void deinitialize(bool zero, T)(ref T value)
+{
+ static if (is(T == U[S], U, size_t S))
+ {
+ foreach (ref e; value)
+ {
+ deinitialize!zero(e);
+ }
+ }
+ else
+ {
+ import tanya.memory.op : copy, fill;
+
+ static if (isNested!T)
+ {
+ // Don't override the context pointer.
+ enum size_t size = T.sizeof - (void*).sizeof;
+ }
+ else
+ {
+ enum size_t size = T.sizeof;
+ }
+ static if (zero)
+ {
+ fill!0((cast(void*) &value)[0 .. size]);
+ }
+ else
+ {
+ copy(typeid(T).initializer()[0 .. size], (&value)[0 .. 1]);
+ }
+ }
+}
+
+/**
+ * Moves $(D_PARAM source) into $(D_PARAM target) assuming that
+ * $(D_PARAM target) isn't initialized.
+ *
+ * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places
+ * the $(D_PARAM source) into a valid but unspecified state, which means that
+ * after moving $(D_PARAM source) can be destroyed or assigned a new value, but
+ * accessing it yields an unspecified value. No postblits or destructors are
+ * called. If the $(D_PARAM target) should be destroyed before, use
+ * $(D_PSYMBOL move).
+ *
+ * $(D_PARAM source) and $(D_PARAM target) must be different objects.
+ *
+ * Params:
+ * T = Object type.
+ * source = Source object.
+ * target = Target object.
+ *
+ * See_Also: $(D_PSYMBOL move),
+ * $(D_PSYMBOL hasElaborateCopyConstructor),
+ * $(D_PSYMBOL hasElaborateDestructor).
+ *
+ * Precondition: `&source !is &target`.
+ */
+void moveEmplace(T)(ref T source, ref T target) @system
+in
+{
+ assert(&source !is &target, "Source and target must be different");
+}
+do
+{
+ static if (is(T == struct) || isStaticArray!T)
+ {
+ import tanya.memory.op : copy;
+
+ copy((&source)[0 .. 1], (&target)[0 .. 1]);
+
+ static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T)
+ {
+ static if (__VERSION__ >= 2083) // __traits(isZeroInit) available.
+ {
+ deinitialize!(__traits(isZeroInit, T))(source);
+ }
+ else
+ {
+ if (typeid(T).initializer().ptr is null)
+ {
+ deinitialize!true(source);
+ }
+ else
+ {
+ deinitialize!false(source);
+ }
+ }
+ }
+ }
+ else
+ {
+ target = source;
+ }
+}
+
+///
+@nogc nothrow pure @system unittest
+{
+ static struct S
+ {
+ int member = 5;
+
+ this(this) @nogc nothrow pure @safe
+ {
+ assert(false);
+ }
+ }
+ S source, target = void;
+ moveEmplace(source, target);
+ assert(target.member == 5);
+
+ int x1 = 5, x2;
+ moveEmplace(x1, x2);
+ assert(x2 == 5);
+}
+
+/**
+ * Moves $(D_PARAM source) into $(D_PARAM target) assuming that
+ * $(D_PARAM target) isn't initialized.
+ *
+ * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places
+ * the $(D_PARAM source) into a valid but unspecified state, which means that
+ * after moving $(D_PARAM source) can be destroyed or assigned a new value, but
+ * accessing it yields an unspecified value. $(D_PARAM target) is destroyed before
+ * the new value is assigned. If $(D_PARAM target) isn't initialized and
+ * therefore shouldn't be destroyed, $(D_PSYMBOL moveEmplace) can be used.
+ *
+ * If $(D_PARAM target) isn't specified, $(D_PSYMBOL move) returns the source
+ * as rvalue without calling its copy constructor or destructor.
+ *
+ * $(D_PARAM source) and $(D_PARAM target) are the same object,
+ * $(D_PSYMBOL move) does nothing.
+ *
+ * Params:
+ * T = Object type.
+ * source = Source object.
+ * target = Target object.
+ *
+ * See_Also: $(D_PSYMBOL moveEmplace).
+ */
+void move(T)(ref T source, ref T target)
+{
+ if ((() @trusted => &source is &target)())
+ {
+ return;
+ }
+ static if (hasElaborateDestructor!T)
+ {
+ target.__xdtor();
+ }
+ (() @trusted => moveEmplace(source, target))();
+}
+
+/// ditto
+T move(T)(ref T source) @trusted
+{
+ static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T)
+ {
+ T target = void;
+ moveEmplace(source, target);
+ return target;
+ }
+ else
+ {
+ return source;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static struct S
+ {
+ int member = 5;
+
+ this(this) @nogc nothrow pure @safe
+ {
+ assert(false);
+ }
+ }
+ S source, target = void;
+ move(source, target);
+ assert(target.member == 5);
+ assert(move(target).member == 5);
+
+ int x1 = 5, x2;
+ move(x1, x2);
+ assert(x2 == 5);
+ assert(move(x2) == 5);
+}
+
+/**
+ * Exchanges the values of $(D_PARAM a) and $(D_PARAM b).
+ *
+ * $(D_PSYMBOL swap) moves the contents of $(D_PARAM a) and $(D_PARAM b)
+ * without calling its postblits or destructors.
+ *
+ * Params:
+ * a = The first object.
+ * b = The second object.
+ */
+void swap(T)(ref T a, ref T b) @trusted
+{
+ T tmp = void;
+ moveEmplace(a, tmp);
+ moveEmplace(b, a);
+ moveEmplace(tmp, b);
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ int a = 3, b = 5;
+ swap(a, b);
+ assert(a == 5);
+ assert(b == 3);
+}
+
+/**
+ * Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out)
+ * storage classes.
+ *
+ * $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an
+ * argument list of the same length that can be for example passed to a
+ * function accepting the arguments of this type.
+ *
+ * Params:
+ * args = Argument list.
+ *
+ * Returns: $(D_PARAM args) with their original storage classes.
+ */
+template forward(args...)
+{
+ static if (args.length == 0)
+ {
+ alias forward = AliasSeq!();
+ }
+ else static if (__traits(isRef, args[0]) || __traits(isOut, args[0]))
+ {
+ static if (args.length == 1)
+ {
+ alias forward = args[0];
+ }
+ else
+ {
+ alias forward = AliasSeq!(args[0], forward!(args[1 .. $]));
+ }
+ }
+ else
+ {
+ @property auto forwardOne()
+ {
+ return move(args[0]);
+ }
+ static if (args.length == 1)
+ {
+ alias forward = forwardOne;
+ }
+ else
+ {
+ alias forward = AliasSeq!(forwardOne, forward!(args[1 .. $]));
+ }
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ static assert(is(typeof((int i) { int v = forward!i; })));
+ static assert(is(typeof((ref int i) { int v = forward!i; })));
+ static assert(is(typeof({
+ void f(int i, ref int j, out int k)
+ {
+ f(forward!(i, j, k));
+ }
+ })));
+}
diff --git a/middle/tanya/memory/mallocator.d b/middle/tanya/memory/mallocator.d
new file mode 100644
index 0000000..1103053
--- /dev/null
+++ b/middle/tanya/memory/mallocator.d
@@ -0,0 +1,218 @@
+/* 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/. */
+
+/**
+ * Allocator based on $(D_PSYMBOL malloc), $(D_PSYMBOL realloc) and
+ * $(D_PSYMBOL free).
+ *
+ * Copyright: Eugene Wissner 2017-2019.
+ * 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)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/mallocator.d,
+ * tanya/memory/mallocator.d)
+ */
+module tanya.memory.mallocator;
+
+version (TanyaNative)
+{
+}
+else:
+
+import core.stdc.stdlib;
+import tanya.memory.allocator;
+
+/**
+ * Wrapper for $(D_PSYMBOL malloc)/$(D_PSYMBOL realloc)/$(D_PSYMBOL free) from
+ * the C standard library.
+ */
+final class Mallocator : Allocator
+{
+ private alias MallocType = extern (C) void* function(size_t)
+ @nogc nothrow pure @system;
+ private alias FreeType = extern (C) void function(void*)
+ @nogc nothrow pure @system;
+ private alias ReallocType = extern (C) void* function(void*, size_t)
+ @nogc nothrow pure @system;
+
+ /**
+ * Allocates $(D_PARAM size) bytes of memory.
+ *
+ * Params:
+ * size = Amount of memory to allocate.
+ *
+ * Returns: The pointer to the new allocated memory.
+ */
+ void[] allocate(size_t size) @nogc nothrow pure shared @system
+ {
+ if (size == 0)
+ {
+ return null;
+ }
+ auto p = (cast(MallocType) &malloc)(size + psize);
+
+ return p is null ? null : p[psize .. psize + size];
+ }
+
+ ///
+ @nogc nothrow pure @system unittest
+ {
+ auto p = Mallocator.instance.allocate(20);
+ assert(p.length == 20);
+ Mallocator.instance.deallocate(p);
+
+ p = Mallocator.instance.allocate(0);
+ assert(p.length == 0);
+ }
+
+ /**
+ * Deallocates a memory block.
+ *
+ * Params:
+ * p = A pointer to the memory block to be freed.
+ *
+ * Returns: Whether the deallocation was successful.
+ */
+ bool deallocate(void[] p) @nogc nothrow pure shared @system
+ {
+ if (p !is null)
+ {
+ (cast(FreeType) &free)(p.ptr - psize);
+ }
+ return true;
+ }
+
+ ///
+ @nogc nothrow pure @system unittest
+ {
+ void[] p;
+ assert(Mallocator.instance.deallocate(p));
+
+ p = Mallocator.instance.allocate(10);
+ assert(Mallocator.instance.deallocate(p));
+ }
+
+ /**
+ * Reallocating in place isn't supported.
+ *
+ * Params:
+ * p = A pointer to the memory block.
+ * size = Size of the reallocated block.
+ *
+ * Returns: $(D_KEYWORD false).
+ */
+ bool reallocateInPlace(ref void[] p, size_t size)
+ @nogc nothrow pure shared @system
+ {
+ cast(void) size;
+ return false;
+ }
+
+ ///
+ @nogc nothrow pure @system unittest
+ {
+ void[] p;
+ assert(!Mallocator.instance.reallocateInPlace(p, 8));
+ }
+
+ /**
+ * Increases or decreases the size of a memory block.
+ *
+ * Params:
+ * p = A pointer to the memory block.
+ * size = Size of the reallocated block.
+ *
+ * Returns: Whether the reallocation was successful.
+ */
+ bool reallocate(ref void[] p, size_t size)
+ @nogc nothrow pure shared @system
+ {
+ if (size == 0)
+ {
+ if (deallocate(p))
+ {
+ p = null;
+ return true;
+ }
+ }
+ else if (p is null)
+ {
+ p = allocate(size);
+ return p is null ? false : true;
+ }
+ else
+ {
+ auto r = (cast(ReallocType) &realloc)(p.ptr - psize, size + psize);
+
+ if (r !is null)
+ {
+ p = r[psize .. psize + size];
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ @nogc nothrow pure @system unittest
+ {
+ void[] p;
+
+ assert(Mallocator.instance.reallocate(p, 20));
+ assert(p.length == 20);
+
+ assert(Mallocator.instance.reallocate(p, 30));
+ assert(p.length == 30);
+
+ assert(Mallocator.instance.reallocate(p, 10));
+ assert(p.length == 10);
+
+ assert(Mallocator.instance.reallocate(p, 0));
+ assert(p is null);
+ }
+
+ /**
+ * Returns: The alignment offered.
+ */
+ @property uint alignment() const @nogc nothrow pure @safe shared
+ {
+ return (void*).alignof;
+ }
+
+ static private shared(Mallocator) instantiate() @nogc nothrow @system
+ {
+ if (instance_ is null)
+ {
+ const size = __traits(classInstanceSize, Mallocator) + psize;
+ void* p = malloc(size);
+
+ if (p !is null)
+ {
+ p[psize .. size] = typeid(Mallocator).initializer[];
+ instance_ = cast(shared Mallocator) p[psize .. size].ptr;
+ }
+ }
+ return instance_;
+ }
+
+ /**
+ * Static allocator instance and initializer.
+ *
+ * Returns: The global $(D_PSYMBOL Allocator) instance.
+ */
+ static @property shared(Mallocator) instance() @nogc nothrow pure @system
+ {
+ return (cast(GetPureInstance!Mallocator) &instantiate)();
+ }
+
+ ///
+ @nogc nothrow pure @system unittest
+ {
+ assert(instance is instance);
+ }
+
+ private enum ushort psize = 8;
+
+ private shared static Mallocator instance_;
+}
diff --git a/middle/tanya/memory/mmappool.d b/middle/tanya/memory/mmappool.d
new file mode 100644
index 0000000..05f71c6
--- /dev/null
+++ b/middle/tanya/memory/mmappool.d
@@ -0,0 +1,503 @@
+/* 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/. */
+
+/*
+ * Native allocator.
+ *
+ * Copyright: Eugene Wissner 2016-2019.
+ * 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)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/mmappool.d,
+ * tanya/memory/mmappool.d)
+ */
+module tanya.memory.mmappool;
+
+version (TanyaNative):
+
+import mir.linux._asm.unistd;
+import tanya.memory.allocator;
+import tanya.memory.op;
+import tanya.os.error;
+import tanya.sys.linux.syscall;
+import tanya.sys.posix.mman;
+
+private void* mapMemory(const size_t length) @nogc nothrow pure @system
+{
+ auto p = syscall_(0,
+ length,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0,
+ NR_mmap);
+ return p == -ErrorCode.noMemory ? null : cast(void*) p;
+}
+
+private bool unmapMemory(shared void* addr, const size_t length)
+@nogc nothrow pure @system
+{
+ return syscall_(cast(ptrdiff_t) addr, length, NR_munmap) == 0;
+}
+
+/*
+ * This allocator allocates memory in regions (multiple of 64 KB for example).
+ * Each region is then splitted in blocks. So it doesn't request the memory
+ * from the operating system on each call, but only if there are no large
+ * enough free blocks in the available regions.
+ * Deallocation works in the same way. Deallocation doesn't immediately
+ * gives the memory back to the operating system, but marks the appropriate
+ * block as free and only if all blocks in the region are free, the complete
+ * region is deallocated.
+ *
+ * <pre>
+ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ * | | | | | || | | |
+ * | |prev <----------- | || | | |
+ * | R | B | | B | || R | B | |
+ * | E | L | | L | next E | L | |
+ * | G | O | DATA | O | FREE ---> G | O | DATA |
+ * | I | C | | C | <--- I | C | |
+ * | O | K | | K | prev O | K | |
+ * | N | -----------> next| || N | | |
+ * | | | | | || | | |
+ * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ * </pre>
+ */
+final class MmapPool : Allocator
+{
+ version (none)
+ {
+ @nogc nothrow pure @system invariant
+ {
+ for (auto r = &head; *r !is null; r = &((*r).next))
+ {
+ auto block = cast(Block) (cast(void*) *r + RegionEntry.sizeof);
+ do
+ {
+ assert(block.prev is null || block.prev.next is block);
+ assert(block.next is null || block.next.prev is block);
+ assert(block.region is *r);
+ }
+ while ((block = block.next) !is null);
+ }
+ }
+ }
+
+ /*
+ * Allocates $(D_PARAM size) bytes of memory.
+ *
+ * Params:
+ * size = Amount of memory to allocate.
+ *
+ * Returns: Pointer to the new allocated memory.
+ */
+ void[] allocate(size_t size) @nogc nothrow pure shared @system
+ {
+ if (size == 0)
+ {
+ return null;
+ }
+ const dataSize = addAlignment(size);
+ if (dataSize < size)
+ {
+ return null;
+ }
+
+ void* data = findBlock(dataSize);
+ if (data is null)
+ {
+ data = initializeRegion(dataSize);
+ }
+
+ return data is null ? null : data[0 .. size];
+ }
+
+ /*
+ * Search for a block large enough to keep $(D_PARAM size) and split it
+ * into two blocks if the block is too large.
+ *
+ * Params:
+ * size = Minimum size the block should have (aligned).
+ *
+ * Returns: Data the block points to or $(D_KEYWORD null).
+ */
+ private void* findBlock(const ref size_t size)
+ @nogc nothrow pure shared @system
+ {
+ Block block1;
+ RegionLoop: for (auto r = head; r !is null; r = r.next)
+ {
+ block1 = cast(Block) (cast(void*) r + RegionEntry.sizeof);
+ do
+ {
+ if (block1.free && block1.size >= size)
+ {
+ break RegionLoop;
+ }
+ }
+ while ((block1 = block1.next) !is null);
+ }
+ if (block1 is null)
+ {
+ return null;
+ }
+ else if (block1.size >= size + alignment_ + BlockEntry.sizeof)
+ { // Split the block if needed
+ Block block2 = cast(Block) (cast(void*) block1 + BlockEntry.sizeof + size);
+ block2.prev = block1;
+ block2.next = block1.next;
+ block2.free = true;
+ block2.size = block1.size - BlockEntry.sizeof - size;
+ block2.region = block1.region;
+
+ if (block1.next !is null)
+ {
+ block1.next.prev = block2;
+ }
+ block1.next = block2;
+ block1.size = size;
+ }
+ block1.free = false;
+ block1.region.blocks = block1.region.blocks + 1;
+
+ return cast(void*) block1 + BlockEntry.sizeof;
+ }
+
+ // Merge block with the next one.
+ private void mergeNext(Block block) const @nogc nothrow pure @safe shared
+ {
+ block.size = block.size + BlockEntry.sizeof + block.next.size;
+ if (block.next.next !is null)
+ {
+ block.next.next.prev = block;
+ }
+ block.next = block.next.next;
+ }
+
+ /*
+ * Deallocates a memory block.
+ *
+ * Params:
+ * p = A pointer to the memory block to be freed.
+ *
+ * Returns: Whether the deallocation was successful.
+ */
+ bool deallocate(void[] p) @nogc nothrow pure shared @system
+ {
+ if (p.ptr is null)
+ {
+ return true;
+ }
+
+ Block block = cast(Block) (p.ptr - BlockEntry.sizeof);
+ if (block.region.blocks <= 1)
+ {
+ if (block.region.prev !is null)
+ {
+ block.region.prev.next = block.region.next;
+ }
+ else // Replace the list head. It is being deallocated
+ {
+ head = block.region.next;
+ }
+ if (block.region.next !is null)
+ {
+ block.region.next.prev = block.region.prev;
+ }
+ return unmapMemory(block.region, block.region.size);
+ }
+ // Merge blocks if neigbours are free.
+ if (block.next !is null && block.next.free)
+ {
+ mergeNext(block);
+ }
+ if (block.prev !is null && block.prev.free)
+ {
+ block.prev.size = block.prev.size + BlockEntry.sizeof + block.size;
+ if (block.next !is null)
+ {
+ block.next.prev = block.prev;
+ }
+ block.prev.next = block.next;
+ }
+ else
+ {
+ block.free = true;
+ }
+ block.region.blocks = block.region.blocks - 1;
+ return true;
+ }
+
+ /*
+ * Reallocates a memory block in place if possible or returns
+ * $(D_KEYWORD false). This function cannot be used to allocate or
+ * deallocate memory, so if $(D_PARAM p) is $(D_KEYWORD null) or
+ * $(D_PARAM size) is `0`, it should return $(D_KEYWORD false).
+ *
+ * Params:
+ * p = A pointer to the memory block.
+ * size = Size of the reallocated block.
+ *
+ * Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
+ */
+ bool reallocateInPlace(ref void[] p, size_t size)
+ @nogc nothrow pure shared @system
+ {
+ if (p is null || size == 0)
+ {
+ return false;
+ }
+ if (size <= p.length)
+ {
+ // Leave the block as is.
+ p = p.ptr[0 .. size];
+ return true;
+ }
+ Block block1 = cast(Block) (p.ptr - BlockEntry.sizeof);
+
+ if (block1.size >= size)
+ {
+ // Enough space in the current block.
+ p = p.ptr[0 .. size];
+ return true;
+ }
+ const dataSize = addAlignment(size);
+ const pAlignment = addAlignment(p.length);
+ assert(pAlignment >= p.length, "Invalid memory chunk length");
+ const delta = dataSize - pAlignment;
+
+ if (block1.next is null
+ || !block1.next.free
+ || dataSize < size
+ || block1.next.size + BlockEntry.sizeof < delta)
+ {
+ /* - It is the last block in the region
+ * - The next block isn't free
+ * - The next block is too small
+ * - Requested size is too large
+ */
+ return false;
+ }
+ if (block1.next.size >= delta + alignment_)
+ {
+ // Move size from block2 to block1.
+ block1.next.size = block1.next.size - delta;
+ block1.size = block1.size + delta;
+
+ auto block2 = cast(Block) (p.ptr + dataSize);
+ if (block1.next.next !is null)
+ {
+ block1.next.next.prev = block2;
+ }
+ copyBackward((cast(void*) block1.next)[0 .. BlockEntry.sizeof],
+ (cast(void*) block2)[0 .. BlockEntry.sizeof]);
+ block1.next = block2;
+ }
+ else
+ {
+ // The next block has enough space, but is too small for further
+ // allocations. Merge it with the current block.
+ mergeNext(block1);
+ }
+
+ p = p.ptr[0 .. size];
+ return true;
+ }
+
+ /*
+ * Increases or decreases the size of a memory block.
+ *
+ * Params:
+ * p = A pointer to the memory block.
+ * size = Size of the reallocated block.
+ *
+ * Returns: Whether the reallocation was successful.
+ */
+ bool reallocate(ref void[] p, size_t size)
+ @nogc nothrow pure shared @system
+ {
+ if (size == 0)
+ {
+ if (deallocate(p))
+ {
+ p = null;
+ return true;
+ }
+ return false;
+ }
+ else if (reallocateInPlace(p, size))
+ {
+ return true;
+ }
+ // Can't reallocate in place, allocate a new block,
+ // copy and delete the previous one.
+ void[] reallocP = allocate(size);
+ if (reallocP is null)
+ {
+ return false;
+ }
+ if (p !is null)
+ {
+ copy(p[0 .. p.length < size ? p.length : size], reallocP);
+ deallocate(p);
+ }
+ p = reallocP;
+
+ return true;
+ }
+
+ static private shared(MmapPool) instantiate() @nogc nothrow @system
+ {
+ if (instance_ is null)
+ {
+ const instanceSize = addAlignment(__traits(classInstanceSize,
+ MmapPool));
+
+ Region head; // Will become soon our region list head
+ void* data = initializeRegion(instanceSize, head);
+ if (data !is null)
+ {
+ copy(typeid(MmapPool).initializer, data[0 .. instanceSize]);
+ instance_ = cast(shared MmapPool) data;
+ instance_.head = head;
+ }
+ }
+ return instance_;
+ }
+
+ /*
+ * Static allocator instance and initializer.
+ *
+ * Returns: Global $(D_PSYMBOL MmapPool) instance.
+ */
+ static @property shared(MmapPool) instance() @nogc nothrow pure @system
+ {
+ return (cast(GetPureInstance!MmapPool) &instantiate)();
+ }
+
+ /*
+ * Initializes a region for one element.
+ *
+ * Params:
+ * size = Aligned size of the first data block in the region.
+ * head = Region list head.
+ *
+ * Returns: A pointer to the data.
+ */
+ private static void* initializeRegion(const size_t size, ref Region head)
+ @nogc nothrow pure @system
+ {
+ const regionSize = calculateRegionSize(size);
+ if (regionSize < size)
+ {
+ return null;
+ }
+
+ void* p = mapMemory(regionSize);
+ if (p is null)
+ {
+ return null;
+ }
+
+ Region region = cast(Region) p;
+ region.blocks = 1;
+ region.size = regionSize;
+
+ // Set the pointer to the head of the region list
+ if (head !is null)
+ {
+ head.prev = region;
+ }
+ region.next = head;
+ region.prev = null;
+ head = region;
+
+ // Initialize the data block
+ void* memoryPointer = p + RegionEntry.sizeof;
+ Block block1 = cast(Block) memoryPointer;
+ block1.size = size;
+ block1.free = false;
+
+ // It is what we want to return
+ void* data = memoryPointer + BlockEntry.sizeof;
+
+ // Free block after data
+ memoryPointer = data + size;
+ Block block2 = cast(Block) memoryPointer;
+ block1.prev = block2.next = null;
+ block1.next = block2;
+ block2.prev = block1;
+ block2.size = regionSize - size - RegionEntry.sizeof - BlockEntry.sizeof * 2;
+ block2.free = true;
+ block1.region = block2.region = region;
+
+ return data;
+ }
+
+ private void* initializeRegion(const size_t size)
+ @nogc nothrow pure shared @system
+ {
+ return initializeRegion(size, this.head);
+ }
+
+ /*
+ * Params:
+ * x = Space to be aligned.
+ *
+ * Returns: Aligned size of $(D_PARAM x).
+ */
+ private static size_t addAlignment(const size_t x) @nogc nothrow pure @safe
+ {
+ return (x - 1) / alignment_ * alignment_ + alignment_;
+ }
+
+ /*
+ * Params:
+ * x = Required space.
+ *
+ * Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
+ */
+ private static size_t calculateRegionSize(ref const size_t x)
+ @nogc nothrow pure @safe
+ {
+ return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2)
+ / pageSize * pageSize + pageSize;
+ }
+
+ /*
+ * Returns: Alignment offered.
+ */
+ @property uint alignment() const @nogc nothrow pure @safe shared
+ {
+ return alignment_;
+ }
+
+ private enum uint alignment_ = 8;
+
+ private shared static MmapPool instance_;
+
+ // Page size.
+ enum size_t pageSize = 65536;
+
+ private shared struct RegionEntry
+ {
+ Region prev;
+ Region next;
+ uint blocks;
+ size_t size;
+ }
+ private alias Region = shared RegionEntry*;
+ private shared Region head;
+
+ private shared struct BlockEntry
+ {
+ Block prev;
+ Block next;
+ Region region;
+ size_t size;
+ bool free;
+ }
+ private alias Block = shared BlockEntry*;
+}
diff --git a/middle/tanya/memory/op.d b/middle/tanya/memory/op.d
new file mode 100644
index 0000000..55a0676
--- /dev/null
+++ b/middle/tanya/memory/op.d
@@ -0,0 +1,375 @@
+/* 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/. */
+
+/**
+ * Set of operations on memory blocks.
+ *
+ * Copyright: Eugene Wissner 2017-2019.
+ * 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)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/op.d,
+ * tanya/memory/op.d)
+ */
+module tanya.memory.op;
+
+version (TanyaNative)
+{
+ extern private void fillMemory(void[], size_t) pure nothrow @system @nogc;
+
+ extern private void copyMemory(const void[], void[])
+ pure nothrow @system @nogc;
+
+ extern private void moveMemory(const void[], void[])
+ pure nothrow @system @nogc;
+
+ extern private bool equalMemory(const void[], const void[])
+ pure nothrow @system @nogc;
+}
+else
+{
+ import core.stdc.string;
+}
+
+private enum alignMask = size_t.sizeof - 1;
+
+/**
+ * Copies $(D_PARAM source) into $(D_PARAM target).
+ *
+ * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that
+ * $(D_PARAM source) points ahead of $(D_PARAM target).
+ *
+ * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length)
+ * elements.
+ *
+ * Params:
+ * source = Memory to copy from.
+ * target = Destination memory.
+ *
+ * See_Also: $(D_PSYMBOL copyBackward).
+ *
+ * Precondition: $(D_INLINECODE source.length <= target.length).
+ */
+void copy(const void[] source, void[] target) @nogc nothrow pure @trusted
+in
+{
+ assert(source.length <= target.length);
+ assert(source.length == 0 || source.ptr !is null);
+ assert(target.length == 0 || target.ptr !is null);
+}
+do
+{
+ version (TanyaNative)
+ {
+ copyMemory(source, target);
+ }
+ else
+ {
+ memcpy(target.ptr, source.ptr, source.length);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ ubyte[9] source = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+ ubyte[9] target;
+ source.copy(target);
+ assert(equal(source, target));
+}
+
+/*
+ * size_t value each of which bytes is set to `Byte`.
+ */
+private template filledBytes(ubyte Byte, ubyte I = 0)
+{
+ static if (I == size_t.sizeof)
+ {
+ enum size_t filledBytes = Byte;
+ }
+ else
+ {
+ enum size_t filledBytes = (filledBytes!(Byte, I + 1) << 8) | Byte;
+ }
+}
+
+/**
+ * Fills $(D_PARAM memory) with the single byte $(D_PARAM c).
+ *
+ * Param:
+ * c = The value to fill $(D_PARAM memory) with.
+ * memory = Memory block.
+ */
+void fill(ubyte c = 0)(void[] memory) @trusted
+in
+{
+ assert(memory.length == 0 || memory.ptr !is null);
+}
+do
+{
+ version (TanyaNative)
+ {
+ fillMemory(memory, filledBytes!c);
+ }
+ else
+ {
+ memset(memory.ptr, c, memory.length);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ ubyte[9] memory = [1, 2, 3, 4, 5, 6, 7, 8, 9];
+ memory.fill!0();
+ foreach (ubyte v; memory)
+ {
+ assert(v == 0);
+ }
+}
+
+/**
+ * Copies starting from the end of $(D_PARAM source) into the end of
+ * $(D_PARAM target).
+ *
+ * $(D_PSYMBOL copyBackward) copies the elements in reverse order, but the
+ * order of elements in the $(D_PARAM target) is exactly the same as in the
+ * $(D_PARAM source).
+ *
+ * $(D_PARAM source) and $(D_PARAM target) shall not overlap so that
+ * $(D_PARAM target) points ahead of $(D_PARAM source).
+ *
+ * $(D_PARAM target) shall have enough space for $(D_INLINECODE source.length)
+ * elements.
+ *
+ * Params:
+ * source = Memory to copy from.
+ * target = Destination memory.
+ *
+ * See_Also: $(D_PSYMBOL copy).
+ *
+ * Precondition: $(D_INLINECODE source.length <= target.length).
+ */
+void copyBackward(const void[] source, void[] target) @nogc nothrow pure @trusted
+in
+{
+ assert(source.length <= target.length);
+ assert(source.length == 0 || source.ptr !is null);
+ assert(target.length == 0 || target.ptr !is null);
+}
+do
+{
+ version (TanyaNative)
+ {
+ moveMemory(source, target);
+ }
+ else
+ {
+ memmove(target.ptr, source.ptr, source.length);
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ ubyte[6] mem = [ 'a', 'a', 'b', 'b', 'c', 'c' ];
+ ubyte[6] expected = [ 'a', 'a', 'a', 'a', 'b', 'b' ];
+
+ copyBackward(mem[0 .. 4], mem[2 .. $]);
+ assert(equal(expected, mem));
+}
+
+/**
+ * Finds the first occurrence of $(D_PARAM needle) in $(D_PARAM haystack) if
+ * any.
+ *
+ * Params:
+ * haystack = Memory block.
+ * needle = A byte.
+ *
+ * Returns: The subrange of $(D_PARAM haystack) whose first element is the
+ * first occurrence of $(D_PARAM needle). If $(D_PARAM needle)
+ * couldn't be found, an empty `inout void[]` is returned.
+ */
+inout(void[]) find(return inout void[] haystack, ubyte needle)
+@nogc nothrow pure @trusted
+in
+{
+ assert(haystack.length == 0 || haystack.ptr !is null);
+}
+do
+{
+ auto length = haystack.length;
+ const size_t needleWord = size_t.max * needle;
+ enum size_t highBits = filledBytes!(0x01, 0);
+ enum size_t mask = filledBytes!(0x80, 0);
+
+ // Align
+ auto bytes = cast(inout(ubyte)*) haystack;
+ while (length > 0 && ((cast(size_t) bytes) & 3) != 0)
+ {
+ if (*bytes == needle)
+ {
+ return bytes[0 .. length];
+ }
+ ++bytes;
+ --length;
+ }
+
+ // Check if some of the words has the needle
+ auto words = cast(inout(size_t)*) bytes;
+ while (length >= size_t.sizeof)
+ {
+ if ((((*words ^ needleWord) - highBits) & (~*words) & mask) != 0)
+ {
+ break;
+ }
+ ++words;
+ length -= size_t.sizeof;
+ }
+
+ // Find the exact needle position in the word
+ bytes = cast(inout(ubyte)*) words;
+ while (length > 0)
+ {
+ if (*bytes == needle)
+ {
+ return bytes[0 .. length];
+ }
+ ++bytes;
+ --length;
+ }
+
+ return haystack[$ .. $];
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h'];
+
+ assert(equal(find(haystack, 'a'), haystack[]));
+ assert(equal(find(haystack, 'b'), haystack[1 .. $]));
+ assert(equal(find(haystack, 'c'), haystack[2 .. $]));
+ assert(equal(find(haystack, 'd'), haystack[3 .. $]));
+ assert(equal(find(haystack, 'e'), haystack[4 .. $]));
+ assert(equal(find(haystack, 'f'), haystack[5 .. $]));
+ assert(equal(find(haystack, 'h'), haystack[8 .. $]));
+ assert(find(haystack, 'i').length == 0);
+
+ assert(find(null, 'a').length == 0);
+}
+
+/**
+ * Looks for `\0` in the $(D_PARAM haystack) and returns the part of the
+ * $(D_PARAM haystack) ahead of it.
+ *
+ * Returns $(D_KEYWORD null) if $(D_PARAM haystack) doesn't contain a null
+ * character.
+ *
+ * Params:
+ * haystack = Memory block.
+ *
+ * Returns: The subrange that spans all bytes before the null character or
+ * $(D_KEYWORD null) if the $(D_PARAM haystack) doesn't contain any.
+ */
+inout(char[]) findNullTerminated(return inout char[] haystack)
+@nogc nothrow pure @trusted
+in
+{
+ assert(haystack.length == 0 || haystack.ptr !is null);
+}
+do
+{
+ auto length = haystack.length;
+ enum size_t highBits = filledBytes!(0x01, 0);
+ enum size_t mask = filledBytes!(0x80, 0);
+
+ // Align
+ auto bytes = cast(inout(ubyte)*) haystack;
+ while (length > 0 && ((cast(size_t) bytes) & 3) != 0)
+ {
+ if (*bytes == '\0')
+ {
+ return haystack[0 .. haystack.length - length];
+ }
+ ++bytes;
+ --length;
+ }
+
+ // Check if some of the words contains 0
+ auto words = cast(inout(size_t)*) bytes;
+ while (length >= size_t.sizeof)
+ {
+ if (((*words - highBits) & (~*words) & mask) != 0)
+ {
+ break;
+ }
+ ++words;
+ length -= size_t.sizeof;
+ }
+
+ // Find the exact 0 position in the word
+ bytes = cast(inout(ubyte)*) words;
+ while (length > 0)
+ {
+ if (*bytes == '\0')
+ {
+ return haystack[0 .. haystack.length - length];
+ }
+ ++bytes;
+ --length;
+ }
+
+ return null;
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ assert(equal(findNullTerminated("abcdef\0gh"), "abcdef"));
+ assert(equal(findNullTerminated("\0garbage"), ""));
+ assert(equal(findNullTerminated("\0"), ""));
+ assert(equal(findNullTerminated("cstring\0"), "cstring"));
+ assert(findNullTerminated(null) is null);
+ assert(findNullTerminated("abcdef") is null);
+}
+
+/**
+ * Compares two memory areas $(D_PARAM r1) and $(D_PARAM r2) for equality.
+ *
+ * Params:
+ * r1 = First memory block.
+ * r2 = Second memory block.
+ *
+ * Returns: $(D_KEYWORD true) if $(D_PARAM r1) and $(D_PARAM r2) are equal,
+ * $(D_KEYWORD false) otherwise.
+ */
+bool equal(const void[] r1, const void[] r2) @nogc nothrow pure @trusted
+in
+{
+ assert(r1.length == 0 || r1.ptr !is null);
+ assert(r2.length == 0 || r2.ptr !is null);
+}
+do
+{
+ version (TanyaNative)
+ {
+ return equalMemory(r1, r2);
+ }
+ else
+ {
+ return r1.length == r2.length
+ && memcmp(r1.ptr, r2.ptr, r1.length) == 0;
+ }
+}
+
+///
+@nogc nothrow pure @safe unittest
+{
+ assert(equal("asdf", "asdf"));
+ assert(!equal("asd", "asdf"));
+ assert(!equal("asdf", "asd"));
+ assert(!equal("asdf", "qwer"));
+}
diff --git a/middle/tanya/memory/package.d b/middle/tanya/memory/package.d
new file mode 100644
index 0000000..c87eeb9
--- /dev/null
+++ b/middle/tanya/memory/package.d
@@ -0,0 +1,132 @@
+/* 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/. */
+
+/**
+ * Dynamic memory management.
+ *
+ * Copyright: Eugene Wissner 2016-2019.
+ * 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)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/package.d,
+ * tanya/memory/package.d)
+ */
+module tanya.memory;
+
+public import tanya.memory.allocator;
+public import tanya.memory.lifetime;
+import tanya.meta.trait;
+deprecated("Use tanya.meta.trait.stateSize instead")
+public import tanya.meta.trait : stateSize;
+
+/**
+ * The mixin generates common methods for classes and structs using
+ * allocators. It provides a protected member, constructor and a read-only property,
+ * that checks if an allocator was already set and sets it to the default
+ * one, if not (useful for structs which don't have a default constructor).
+ */
+mixin template DefaultAllocator()
+{
+ /// Allocator.
+ protected shared Allocator allocator_;
+
+ /**
+ * Params:
+ * allocator = The allocator should be used.
+ *
+ * Precondition: $(D_INLINECODE allocator_ !is null)
+ */
+ this(shared Allocator allocator) @nogc nothrow pure @safe
+ in (allocator !is null)
+ {
+ this.allocator_ = allocator;
+ }
+
+ /**
+ * This property checks if the allocator was set in the constructor
+ * and sets it to the default one, if not.
+ *
+ * Returns: Used allocator.
+ *
+ * Postcondition: $(D_INLINECODE allocator !is null)
+ */
+ @property shared(Allocator) allocator() @nogc nothrow pure @safe
+ out (allocator; allocator !is null)
+ {
+ if (allocator_ is null)
+ {
+ allocator_ = defaultAllocator;
+ }
+ return allocator_;
+ }
+
+ /// ditto
+ @property shared(Allocator) allocator() const @nogc nothrow pure @trusted
+ out (allocator; allocator !is null)
+ {
+ if (allocator_ is null)
+ {
+ return defaultAllocator;
+ }
+ return cast(shared Allocator) allocator_;
+ }
+}
+
+shared Allocator allocator;
+
+private shared(Allocator) getAllocatorInstance() @nogc nothrow
+{
+ if (allocator is null)
+ {
+ version (TanyaNative)
+ {
+ import tanya.memory.mmappool;
+ defaultAllocator = MmapPool.instance;
+ }
+ else
+ {
+ import tanya.memory.mallocator;
+ defaultAllocator = Mallocator.instance;
+ }
+ }
+ return allocator;
+}
+
+/**
+ * Returns: Default allocator.
+ *
+ * Postcondition: $(D_INLINECODE allocator !is null).
+ */
+@property shared(Allocator) defaultAllocator() @nogc nothrow pure @trusted
+out (allocator; allocator !is null)
+{
+ return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
+}
+
+/**
+ * Sets the default allocator.
+ *
+ * Params:
+ * allocator = $(D_PSYMBOL Allocator) instance.
+ *
+ * Precondition: $(D_INLINECODE allocator !is null).
+ */
+@property void defaultAllocator(shared(Allocator) allocator) @nogc nothrow @safe
+in (allocator !is null)
+{
+ .allocator = allocator;
+}
+
+/**
+ * Params:
+ * size = Raw size.
+ * alignment = Alignment.
+ *
+ * Returns: Aligned size.
+ */
+size_t alignedSize(const size_t size, const size_t alignment = 8)
+pure nothrow @safe @nogc
+{
+ return (size - 1) / alignment * alignment + alignment;
+}
diff --git a/middle/tanya/memory/smartref.d b/middle/tanya/memory/smartref.d
new file mode 100644
index 0000000..953513e
--- /dev/null
+++ b/middle/tanya/memory/smartref.d
@@ -0,0 +1,634 @@
+/* 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.
+ *
+ * A smart pointer is an object that wraps a raw pointer or a reference
+ * (class, dynamic array) to manage its lifetime.
+ *
+ * This module provides two kinds of lifetime management strategies:
+ * $(UL
+ * $(LI Reference counting)
+ * $(LI Unique ownership)
+ * )
+ *
+ * Copyright: Eugene Wissner 2016-2019.
+ * 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)
+ * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/middle/tanya/memory/smartref.d,
+ * tanya/memory/smartref.d)
+ */
+module tanya.memory.smartref;
+
+import tanya.memory;
+import tanya.meta.trait;
+
+private template Payload(T)
+{
+ static if (isPolymorphicType!T || isDynamicArray!T)
+ {
+ alias Payload = T;
+ }
+ else
+ {
+ alias Payload = T*;
+ }
+}
+
+private final class RefCountedStore(T)
+{
+ T payload;
+ size_t counter = 1;
+
+ size_t opUnary(string op)()
+ if (op == "--" || op == "++")
+ in (this.counter > 0)
+ {
+ 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;
+ }
+ }
+}
+
+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(this.storage is null || this.allocator_ !is null);
+ assert(this.storage is null || this.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(Payload!T value, shared Allocator allocator = defaultAllocator)
+ {
+ this(allocator);
+ this.storage = allocator.make!Storage();
+ this.deleter = &separateDeleter!(Payload!T);
+
+ this.storage.payload = value;
+ }
+
+ /// ditto
+ this(shared Allocator allocator)
+ in (allocator !is null)
+ {
+ 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 > 0 && --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(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;
+ }
+ this.storage.payload = rhs;
+ 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
+ in (count > 0, "Attempted to access an uninitialized reference")
+ {
+ return this.storage.payload;
+ }
+
+ version (D_Ddoc)
+ {
+ /**
+ * Dereferences the pointer. It is defined only for pointers, not for
+ * reference types like classes, that can be accessed directly.
+ *
+ * Params:
+ * op = Operation.
+ *
+ * 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 *this.storage.payload;
+ }
+ }
+
+ /**
+ * Returns: Whether this $(D_PSYMBOL RefCounted) already has an internal
+ * storage.
+ */
+ @property bool isInitialized() const
+ {
+ return this.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 this.storage is null ? 0 : this.storage.counter;
+ }
+
+ mixin DefaultAllocator;
+ alias get this;
+}
+
+///
+@nogc @system unittest
+{
+ auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator);
+ auto val = rc.get();
+
+ *val = 8;
+ assert(*rc.get == 8);
+
+ val = null;
+ assert(rc.get !is null);
+ assert(*rc.get == 8);
+
+ *rc = 9;
+ assert(*rc.get == 9);
+}
+
+/**
+ * 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 (allocator !is null)
+{
+ 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]);
+ rc.storage.payload = emplace!T(mem[storageSize .. $], 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.
+ * E = Array element type.
+ * size = Array size.
+ * allocator = Allocator.
+ *
+ * Returns: Newly created $(D_PSYMBOL RefCounted!T).
+ *
+ * Precondition: $(D_INLINECODE allocator !is null
+ * && size <= size_t.max / E.sizeof)
+ */
+RefCounted!T refCounted(T : E[], E)(shared Allocator allocator, size_t size)
+@trusted
+in (allocator !is null)
+in (size <= size_t.max / E.sizeof)
+{
+ return RefCounted!T(allocator.make!T(size), allocator);
+}
+
+///
+@nogc @system 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);
+}
+
+/**
+ * $(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(Payload!T value, shared Allocator allocator = defaultAllocator)
+ {
+ this(allocator);
+ this.payload = value;
+ }
+
+ /// ditto
+ this(shared Allocator allocator)
+ in (allocator !is null)
+ {
+ this.allocator_ = allocator;
+ }
+
+ /**
+ * $(D_PSYMBOL Unique) is noncopyable.
+ */
+ @disable this(this);
+
+ /**
+ * Destroys the owned object.
+ */
+ ~this()
+ {
+ 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(Payload!T rhs)
+ {
+ allocator.dispose(this.payload);
+ this.payload = rhs;
+ 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;
+ }
+
+ ///
+ @nogc nothrow pure @system unittest
+ {
+ auto rc = defaultAllocator.unique!int(5);
+ rc = defaultAllocator.make!int(7);
+ assert(*rc == 7);
+ }
+
+ /**
+ * Returns: Reference to the owned object.
+ */
+ inout(Payload!T) get() inout
+ {
+ return this.payload;
+ }
+
+ version (D_Ddoc)
+ {
+ /**
+ * Dereferences the pointer. It is defined only for pointers, not for
+ * reference types like classes, that can be accessed directly.
+ *
+ * Params:
+ * op = Operation.
+ *
+ * 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 *this.payload;
+ }
+ }
+
+ /**
+ * Returns: Whether this $(D_PSYMBOL Unique) holds some value.
+ */
+ @property bool isInitialized() const
+ {
+ return this.payload !is null;
+ }
+
+ ///
+ @nogc nothrow pure @system unittest
+ {
+ Unique!int u;
+ assert(!u.isInitialized);
+ }
+
+ /**
+ * Sets the internal pointer to $(D_KEYWORD). The allocator isn't changed.
+ *
+ * Returns: Reference to the owned object.
+ */
+ Payload!T release()
+ {
+ auto payload = this.payload;
+ this.payload = null;
+ return payload;
+ }
+
+ ///
+ @nogc nothrow pure @system unittest
+ {
+ auto u = defaultAllocator.unique!int(5);
+ assert(u.isInitialized);
+
+ auto i = u.release();
+ assert(*i == 5);
+ assert(!u.isInitialized);
+ }
+
+ mixin DefaultAllocator;
+ alias get this;
+}
+
+///
+@nogc nothrow pure @system unittest
+{
+ auto p = defaultAllocator.make!int(5);
+ auto s = Unique!int(p, defaultAllocator);
+ assert(*s == 5);
+}
+
+///
+@nogc nothrow @system unittest
+{
+ static bool destroyed;
+
+ static struct F
+ {
+ ~this() @nogc nothrow @safe
+ {
+ 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 (allocator !is null)
+{
+ auto payload = allocator.make!(T, 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.
+ * E = Array element type.
+ * size = Array size.
+ * allocator = Allocator.
+ *
+ * Returns: Newly created $(D_PSYMBOL Unique!T).
+ *
+ * Precondition: $(D_INLINECODE allocator !is null
+ * && size <= size_t.max / E.sizeof)
+ */
+Unique!T unique(T : E[], E)(shared Allocator allocator, size_t size)
+@trusted
+in (allocator !is null)
+in (size <= size_t.max / E.sizeof)
+{
+ auto payload = allocator.resize!E(null, size);
+ return Unique!T(payload, allocator);
+}