From 5ab99cf8873b130284336c52551ccede28f6cb2e Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 17 Mar 2019 10:56:44 +0100 Subject: Move memory functions into memory.lifecycle - move - moveEmplace - forward - emplace - swap --- source/tanya/algorithm/iteration.d | 2 +- source/tanya/algorithm/mutation.d | 289 +----------- source/tanya/container/array.d | 1 - source/tanya/container/entry.d | 2 +- source/tanya/container/list.d | 1 - source/tanya/conv.d | 271 +---------- source/tanya/functional.d | 68 +-- source/tanya/memory/allocator.d | 81 ---- source/tanya/memory/lifecycle.d | 360 --------------- source/tanya/memory/mallocator.d | 233 ---------- source/tanya/memory/mmappool.d | 657 -------------------------- source/tanya/memory/op.d | 437 ------------------ source/tanya/memory/package.d | 203 -------- source/tanya/memory/smartref.d | 914 ------------------------------------- source/tanya/net/ip.d | 1 + source/tanya/range/adapter.d | 2 +- source/tanya/range/primitive.d | 2 +- source/tanya/test/assertion.d | 105 ----- source/tanya/test/package.d | 18 - source/tanya/test/stub.d | 373 --------------- source/tanya/typecons.d | 4 +- 21 files changed, 23 insertions(+), 4001 deletions(-) delete mode 100644 source/tanya/memory/allocator.d delete mode 100644 source/tanya/memory/lifecycle.d delete mode 100644 source/tanya/memory/mallocator.d delete mode 100644 source/tanya/memory/mmappool.d delete mode 100644 source/tanya/memory/op.d delete mode 100644 source/tanya/memory/package.d delete mode 100644 source/tanya/memory/smartref.d delete mode 100644 source/tanya/test/assertion.d delete mode 100644 source/tanya/test/package.d delete mode 100644 source/tanya/test/stub.d (limited to 'source') diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index 7eae9cd..fcfb1c0 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -21,7 +21,7 @@ module tanya.algorithm.iteration; import tanya.algorithm.comparison; -import tanya.algorithm.mutation; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.range; diff --git a/source/tanya/algorithm/mutation.d b/source/tanya/algorithm/mutation.d index f0c156d..ddb2743 100644 --- a/source/tanya/algorithm/mutation.d +++ b/source/tanya/algorithm/mutation.d @@ -14,284 +14,21 @@ */ module tanya.algorithm.mutation; -import tanya.conv; static import tanya.memory.op; +static import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.range; version (unittest) import tanya.test.stub; -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 - { - 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) - { - tanya.memory.op.fill!0((cast(void*) &value)[0 .. size]); - } - else - { - tanya.memory.op.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) - { - tanya.memory.op.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); +deprecated("Use tanya.memory.lifecycle.swap instead") +alias swap = tanya.memory.lifecycle.swap; - int x1 = 5, x2; - moveEmplace(x1, x2); - assert(x2 == 5); -} +deprecated("Use tanya.memory.lifecycle.moveEmplace instead") +alias moveEmplace = tanya.memory.lifecycle.moveEmplace; -// Is pure. -@nogc nothrow pure @system unittest -{ - struct S - { - this(this) - { - } - } - S source, target = void; - static assert(is(typeof({ moveEmplace(source, target); }))); -} - -// Moves nested. -@nogc nothrow pure @system unittest -{ - struct Nested - { - void method() @nogc nothrow pure @safe - { - } - } - Nested source, target = void; - moveEmplace(source, target); - assert(source == target); -} - -// Emplaces static arrays. -@nogc nothrow pure @system unittest -{ - static struct S - { - size_t member; - this(size_t i) @nogc nothrow pure @safe - { - this.member = i; - } - ~this() @nogc nothrow pure @safe - { - } - } - S[2] source = [ S(5), S(5) ], target = void; - moveEmplace(source, target); - assert(source[0].member == 0); - assert(target[0].member == 5); - assert(source[1].member == 0); - assert(target[1].member == 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); -} - -// Moves if source is target. -@nogc nothrow pure @safe unittest -{ - int x = 5; - move(x, x); - assert(x == 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); -} +deprecated("Use tanya.memory.lifecycle.move instead") +alias move = tanya.memory.lifecycle.move; /** * Copies the $(D_PARAM source) range into the $(D_PARAM target) range. @@ -494,7 +231,7 @@ if (isInputRange!Range && hasLvalueElements!Range for (; !range.empty; range.popFront()) { ElementType!Range* p = &range.front; - emplace!(ElementType!Range)(cast(void[]) (p[0 .. 1]), value); + tanya.memory.lifecycle.emplace!(ElementType!Range)(cast(void[]) (p[0 .. 1]), value); } } else @@ -577,13 +314,7 @@ if (isInputRange!Range && hasLvalueElements!Range) void destroyAll(Range)(Range range) if (isInputRange!Range && hasLvalueElements!Range) { - static if (hasElaborateDestructor!(ElementType!Range)) - { - foreach (ref e; range) - { - destroy(e); - } - } + tanya.memory.lifecycle.destroyAllImpl!(Range, ElementType!Range)(range); } /// @@ -632,7 +363,7 @@ if (isForwardRange!Range && hasSwappableElements!Range) while (!front.empty && !next.empty && !sameHead(front, next)) { - swap(front.front, next.front); + tanya.memory.lifecycle.swap(front.front, next.front); front.popFront(); next.popFront(); diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d index 2b3adfa..1c6842f 100644 --- a/source/tanya/container/array.d +++ b/source/tanya/container/array.d @@ -17,7 +17,6 @@ module tanya.container.array; import core.checkedint; import tanya.algorithm.comparison; import tanya.algorithm.mutation; -import tanya.functional; import tanya.memory; import tanya.meta.trait; import tanya.meta.transform; diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d index 1e7e28f..408849a 100644 --- a/source/tanya/container/entry.d +++ b/source/tanya/container/entry.d @@ -14,9 +14,9 @@ */ module tanya.container.entry; -import tanya.algorithm.mutation; import tanya.container.array; import tanya.memory.allocator; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.typecons; diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index 09a5c9f..e6e01d5 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -16,7 +16,6 @@ module tanya.container.list; import tanya.algorithm.comparison; -import tanya.algorithm.mutation; import tanya.container.entry; import tanya.memory; import tanya.meta.trait; diff --git a/source/tanya/conv.d b/source/tanya/conv.d index a78c2bb..f602e01 100644 --- a/source/tanya/conv.d +++ b/source/tanya/conv.d @@ -14,15 +14,13 @@ */ module tanya.conv; -import tanya.algorithm.mutation; import tanya.container.string; -import tanya.format; import tanya.memory; -import tanya.memory.op; +deprecated("Use tanya.memory.lifecycle.emplace instead") +public import tanya.memory.lifecycle : emplace; import tanya.meta.trait; import tanya.meta.transform; -import tanya.range.array; -import tanya.range.primitive; +import tanya.range; version (unittest) { @@ -30,269 +28,6 @@ version (unittest) import tanya.test.stub; } -/** - * 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)()) -{ - 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)()) -{ - 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 -{ - import tanya.memory : stateSize; - - 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 -{ - 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]; }))) - { - ((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); -} - -// Handles "Cannot access frame pointer" error. -@nogc nothrow pure @safe unittest -{ - struct F - { - ~this() @nogc nothrow pure @safe - { - } - } - static assert(is(typeof(emplace!F((void[]).init)))); -} - -// Can emplace structs without a constructor -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof(emplace!WithDtor(null, WithDtor())))); - static assert(is(typeof(emplace!WithDtor(null)))); -} - -// Doesn't call a destructor on uninitialized elements -@nogc nothrow pure @system unittest -{ - static struct SWithDtor - { - private bool canBeInvoked = false; - ~this() @nogc nothrow pure @safe - { - assert(this.canBeInvoked); - } - } - void[SWithDtor.sizeof] memory = void; - auto actual = emplace!SWithDtor(memory[], SWithDtor(true)); - assert(actual.canBeInvoked); -} - -// Initializes structs if no arguments are given -@nogc nothrow pure @safe unittest -{ - static struct SEntry - { - byte content; - } - ubyte[1] mem = [3]; - - assert(emplace!SEntry(cast(void[]) mem[0 .. 1]).content == 0); -} - -// Postblit is called when emplacing a struct -@nogc nothrow pure @system unittest -{ - static struct S - { - bool called = false; - this(this) @nogc nothrow pure @safe - { - this.called = true; - } - } - S target; - S* sp = ⌖ - - emplace!S(sp[0 .. 1], S()); - assert(target.called); -} - /** * Thrown if a type conversion fails. */ diff --git a/source/tanya/functional.d b/source/tanya/functional.d index f628a51..9a1c3a1 100644 --- a/source/tanya/functional.d +++ b/source/tanya/functional.d @@ -2,77 +2,17 @@ * 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/. */ -/** +/* * Functions that manipulate other functions and their argument lists. * - * Copyright: Eugene Wissner 2018. + * Copyright: Eugene Wissner 2018-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/source/tanya/functional.d, * tanya/functional.d) */ +deprecated("Use tanya.memory.lifecycle instead") module tanya.functional; -import tanya.algorithm.mutation; -import tanya.meta.metafunction; - -/** - * 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)); - } - }))); -} +public import tanya.memory.lifecycle : forward; diff --git a/source/tanya/memory/allocator.d b/source/tanya/memory/allocator.d deleted file mode 100644 index 3d05a37..0000000 --- a/source/tanya/memory/allocator.d +++ /dev/null @@ -1,81 +0,0 @@ -/* 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/source/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/source/tanya/memory/lifecycle.d b/source/tanya/memory/lifecycle.d deleted file mode 100644 index 2221af3..0000000 --- a/source/tanya/memory/lifecycle.d +++ /dev/null @@ -1,360 +0,0 @@ -/* 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/. */ - -/** - * Lifecycle 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/source/tanya/memory/init.d, - * tanya/memory/init.d) - */ -module tanya.memory.lifecycle; - -import tanya.algorithm.mutation; -import tanya.conv; -import tanya.memory; -import tanya.meta.trait; -import tanya.range.primitive; - -/** - * 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; -} - -@nogc nothrow pure @safe unittest -{ - int[] p; - - p = defaultAllocator.resize(p, 20); - assert(p.length == 20); - - p = defaultAllocator.resize(p, 30); - assert(p.length == 30); - - p = defaultAllocator.resize(p, 10); - assert(p.length == 10); - - p = defaultAllocator.resize(p, 0); - assert(p is null); -} - -/* - * Destroys the object. - * Returns the memory should be freed. - */ -package(tanya) 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) 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) void[] finalize(T)(ref T[] p) -{ - destroyAll(p); - return p; -} - -/** - * 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; -} - -@nogc nothrow pure @system unittest -{ - static struct S - { - ~this() @nogc nothrow pure @safe - { - } - } - auto p = cast(S[]) defaultAllocator.allocate(S.sizeof); - - defaultAllocator.dispose(p); -} - -// Works with interfaces. -@nogc nothrow pure @safe unittest -{ - interface I - { - } - class C : I - { - } - auto c = defaultAllocator.make!C(); - I i = c; - - defaultAllocator.dispose(i); - defaultAllocator.dispose(i); -} - -/** - * 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 (!is(T == interface) - && !is(T == class) - && !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. - * allocator = Allocator. - * n = Array size. - * - * Returns: Newly created array. - * - * Precondition: $(D_INLINECODE allocator !is null - * && n <= size_t.max / ElementType!T.sizeof) - */ -T make(T)(shared Allocator allocator, const size_t n) -if (isArray!T) -in (allocator !is null) -in (n <= size_t.max / ElementType!T.sizeof) -{ - auto ret = allocator.resize!(ElementType!T)(null, n); - ret.uninitializedFill(ElementType!T.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); -} diff --git a/source/tanya/memory/mallocator.d b/source/tanya/memory/mallocator.d deleted file mode 100644 index fdf7ede..0000000 --- a/source/tanya/memory/mallocator.d +++ /dev/null @@ -1,233 +0,0 @@ -/* 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/source/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); - } - - // Fails with false - @nogc nothrow pure @system unittest - { - void[] p = Mallocator.instance.allocate(20); - void[] oldP = p; - assert(!Mallocator.instance.reallocate(p, size_t.max - Mallocator.psize * 2)); - assert(oldP is p); - Mallocator.instance.deallocate(p); - } - - /** - * Returns: The alignment offered. - */ - @property uint alignment() const @nogc nothrow pure @safe shared - { - return (void*).alignof; - } - - private nothrow @nogc unittest - { - assert(Mallocator.instance.alignment == (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/source/tanya/memory/mmappool.d b/source/tanya/memory/mmappool.d deleted file mode 100644 index 5c42241..0000000 --- a/source/tanya/memory/mmappool.d +++ /dev/null @@ -1,657 +0,0 @@ -/* 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/source/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. - * - *
- * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * |      |     |         |     |            ||      |     |                  |
- * |      |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  |     |                  |
- * |      |     |         |     |            ||      |     |                  |
- * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
- * 
- */ -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]; - } - - @nogc nothrow pure @system unittest - { - auto p = MmapPool.instance.allocate(20); - assert(p); - MmapPool.instance.deallocate(p); - - p = MmapPool.instance.allocate(0); - assert(p.length == 0); - } - - @nogc nothrow pure @system unittest - { - // allocate() check. - size_t tooMuchMemory = size_t.max - - MmapPool.alignment_ - - BlockEntry.sizeof * 2 - - RegionEntry.sizeof - - pageSize; - assert(MmapPool.instance.allocate(tooMuchMemory) is null); - - assert(MmapPool.instance.allocate(size_t.max) is null); - - // initializeRegion() check. - tooMuchMemory = size_t.max - MmapPool.alignment_; - assert(MmapPool.instance.allocate(tooMuchMemory) is null); - } - - /* - * 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; - } - - @nogc nothrow pure @system unittest - { - auto p = MmapPool.instance.allocate(20); - - assert(MmapPool.instance.deallocate(p)); - } - - /* - * 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; - } - - @nogc nothrow pure @system unittest - { - void[] p; - assert(!MmapPool.instance.reallocateInPlace(p, 5)); - assert(p is null); - - p = MmapPool.instance.allocate(1); - auto orig = p.ptr; - - assert(MmapPool.instance.reallocateInPlace(p, 2)); - assert(p.length == 2); - assert(p.ptr == orig); - - assert(MmapPool.instance.reallocateInPlace(p, 4)); - assert(p.length == 4); - assert(p.ptr == orig); - - assert(MmapPool.instance.reallocateInPlace(p, 2)); - assert(p.length == 2); - assert(p.ptr == orig); - - MmapPool.instance.deallocate(p); - } - - /* - * 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 .. min(p.length, size)], reallocP); - deallocate(p); - } - p = reallocP; - - return true; - } - - @nogc nothrow pure @system unittest - { - void[] p; - MmapPool.instance.reallocate(p, 10 * int.sizeof); - (cast(int[]) p)[7] = 123; - - assert(p.length == 40); - - MmapPool.instance.reallocate(p, 8 * int.sizeof); - - assert(p.length == 32); - assert((cast(int[]) p)[7] == 123); - - MmapPool.instance.reallocate(p, 20 * int.sizeof); - (cast(int[]) p)[15] = 8; - - assert(p.length == 80); - assert((cast(int[]) p)[15] == 8); - assert((cast(int[]) p)[7] == 123); - - MmapPool.instance.reallocate(p, 8 * int.sizeof); - - assert(p.length == 32); - assert((cast(int[]) p)[7] == 123); - - MmapPool.instance.deallocate(p); - } - - 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)(); - } - - @nogc nothrow pure @system unittest - { - assert(instance is instance); - } - - /* - * 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_; - } - - @nogc nothrow pure @system unittest - { - assert(MmapPool.instance.alignment == MmapPool.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*; -} - -// A lot of allocations/deallocations, but it is the minimum caused a -// segmentation fault because MmapPool reallocateInPlace moves a block wrong. -@nogc nothrow pure @system unittest -{ - auto a = MmapPool.instance.allocate(16); - auto d = MmapPool.instance.allocate(16); - auto b = MmapPool.instance.allocate(16); - auto e = MmapPool.instance.allocate(16); - auto c = MmapPool.instance.allocate(16); - auto f = MmapPool.instance.allocate(16); - - MmapPool.instance.deallocate(a); - MmapPool.instance.deallocate(b); - MmapPool.instance.deallocate(c); - - a = MmapPool.instance.allocate(50); - MmapPool.instance.reallocateInPlace(a, 64); - MmapPool.instance.deallocate(a); - - a = MmapPool.instance.allocate(1); - auto tmp1 = MmapPool.instance.allocate(1); - auto h1 = MmapPool.instance.allocate(1); - auto tmp2 = cast(ubyte[]) MmapPool.instance.allocate(1); - - auto h2 = MmapPool.instance.allocate(2); - tmp1 = MmapPool.instance.allocate(1); - MmapPool.instance.deallocate(h2); - MmapPool.instance.deallocate(h1); - - h2 = MmapPool.instance.allocate(2); - h1 = MmapPool.instance.allocate(1); - MmapPool.instance.deallocate(h2); - - auto rep = cast(void[]) tmp2; - MmapPool.instance.reallocate(rep, tmp1.length); - tmp2 = cast(ubyte[]) rep; - - MmapPool.instance.reallocate(tmp1, 9); - - rep = cast(void[]) tmp2; - MmapPool.instance.reallocate(rep, tmp1.length); - tmp2 = cast(ubyte[]) rep; - MmapPool.instance.reallocate(tmp1, 17); - - tmp2[$ - 1] = 0; - - MmapPool.instance.deallocate(tmp1); - - b = MmapPool.instance.allocate(16); - - MmapPool.instance.deallocate(h1); - MmapPool.instance.deallocate(a); - MmapPool.instance.deallocate(b); - MmapPool.instance.deallocate(d); - MmapPool.instance.deallocate(e); - MmapPool.instance.deallocate(f); -} diff --git a/source/tanya/memory/op.d b/source/tanya/memory/op.d deleted file mode 100644 index 6005329..0000000 --- a/source/tanya/memory/op.d +++ /dev/null @@ -1,437 +0,0 @@ -/* 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/source/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; -} - -version (TanyaNative) -{ - @nogc nothrow pure @system unittest - { - ubyte[2] buffer = 1; - fillMemory(buffer[1 .. $], 0); - assert(buffer[0] == 1 && buffer[1] == 0); - } - - @nogc nothrow pure @safe unittest - { - assert(equal(null, null)); - } -} - -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)); -} - -@nogc nothrow pure @safe unittest -{ - { - ubyte[0] source, target; - source.copy(target); - } - { - ubyte[1] source = [1]; - ubyte[1] target; - source.copy(target); - assert(target[0] == 1); - } - { - ubyte[8] source = [1, 2, 3, 4, 5, 6, 7, 8]; - ubyte[8] 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)); -} - -@nogc nothrow pure @safe unittest -{ - ubyte[9] r1 = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ]; - ubyte[9] r2; - - copyBackward(r1, r2); - assert(equal(r1, r2)); -} - -/** - * 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")); -} - -// Compares unanligned memory -@nogc nothrow pure @safe unittest -{ - ubyte[16] r1 = [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - ]; - ubyte[16] r2 = [ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - ]; - - assert(equal(r1, r2)); - assert(equal(r1[1 .. $], r2[1 .. $])); - assert(equal(r1[0 .. $ - 1], r2[0 .. $ - 1])); - assert(equal(r1[0 .. 8], r2[0 .. 8])); -} diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d deleted file mode 100644 index 8526b5a..0000000 --- a/source/tanya/memory/package.d +++ /dev/null @@ -1,203 +0,0 @@ -/* 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/source/tanya/memory/package.d, - * tanya/memory/package.d) - */ -module tanya.memory; - -import tanya.conv; -public import tanya.memory.allocator; -public import tanya.memory.lifecycle; -import tanya.meta.trait; - -/** - * 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 - { - assert(allocator !is null); - } - do - { - 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) - { - assert(allocator !is null); - } - do - { - if (allocator_ is null) - { - allocator_ = defaultAllocator; - } - return allocator_; - } - - /// ditto - @property shared(Allocator) allocator() const @nogc nothrow pure @trusted - out (allocator) - { - assert(allocator !is null); - } - do - { - 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) -{ - assert(allocator !is null); -} -do -{ - 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 -{ - assert(allocator !is null); -} -do -{ - .allocator = allocator; -} - -/** - * Returns the size in bytes of the state that needs to be allocated to hold an - * object of type $(D_PARAM T). - * - * There is a difference between the `.sizeof`-property and - * $(D_PSYMBOL stateSize) if $(D_PARAM T) is a class or an interface. - * `T.sizeof` is constant on the given architecture then and is the same as - * `size_t.sizeof` and `ptrdiff_t.sizeof`. This is because classes and - * interfaces are reference types and `.sizeof` returns the size of the - * reference which is the same as the size of a pointer. $(D_PSYMBOL stateSize) - * returns the size of the instance itself. - * - * The size of a dynamic array is `size_t.sizeof * 2` since a dynamic array - * stores its length and a data pointer. The size of the static arrays is - * calculated differently since they are value types. It is the array length - * multiplied by the element size. - * - * `stateSize!void` is `1` since $(D_KEYWORD void) is mostly used as a synonym - * for $(D_KEYWORD byte)/$(D_KEYWORD ubyte) in `void*`. - * - * Params: - * T = Object type. - * - * Returns: Size of an instance of type $(D_PARAM T). - */ -template stateSize(T) -{ - static if (isPolymorphicType!T) - { - enum size_t stateSize = __traits(classInstanceSize, T); - } - else - { - enum size_t stateSize = T.sizeof; - } -} - -/// -@nogc nothrow pure @safe unittest -{ - static assert(stateSize!int == 4); - static assert(stateSize!bool == 1); - static assert(stateSize!(int[]) == (size_t.sizeof * 2)); - static assert(stateSize!(short[3]) == 6); - - static struct Empty - { - } - static assert(stateSize!Empty == 1); - static assert(stateSize!void == 1); -} - -/** - * 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/source/tanya/memory/smartref.d b/source/tanya/memory/smartref.d deleted file mode 100644 index 90271e7..0000000 --- a/source/tanya/memory/smartref.d +++ /dev/null @@ -1,914 +0,0 @@ -/* 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/source/tanya/memory/smartref.d, - * tanya/memory/smartref.d) - */ -module tanya.memory.smartref; - -import tanya.algorithm.mutation; -import tanya.conv; -import tanya.memory; -import tanya.meta.trait; -import tanya.range.primitive; -version (unittest) import tanya.test.stub; - -private template Payload(T) -{ - static if (isPolymorphicType!T || isArray!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 - { - assert(this.counter > 0); - } - do - { - 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 - { - assert(allocator !is null); - } - do - { - 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 - { - assert(count > 0, "Attempted to access an uninitialized reference"); - } - do - { - 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.storage.payload == 8); - - val = null; - assert(rc.storage.payload !is null); - assert(*rc.storage.payload == 8); - - *rc = 9; - assert(*rc.storage.payload == 9); -} - -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!int(5); - rc = defaultAllocator.make!int(7); - assert(*rc == 7); -} - -@nogc @system unittest -{ - RefCounted!int rc; - assert(!rc.isInitialized); - rc = null; - assert(!rc.isInitialized); -} - -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!int(5); - - void func(RefCounted!int param) @nogc - { - assert(param.count == 2); - param = defaultAllocator.make!int(7); - assert(param.count == 1); - assert(*param == 7); - } - func(rc); - assert(rc.count == 1); - assert(*rc == 5); -} - -@nogc @system unittest -{ - RefCounted!int rc; - - void func(RefCounted!int param) @nogc - { - assert(param.count == 0); - param = defaultAllocator.make!int(7); - assert(param.count == 1); - assert(*param == 7); - } - func(rc); - assert(rc.count == 0); -} - -@nogc @system unittest -{ - RefCounted!int rc1, rc2; - static assert(is(typeof(rc1 = rc2))); -} - -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; - } - } -} - -@nogc @system 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 @system - { - 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); -} - -@nogc @system unittest -{ - auto rc = RefCounted!int(defaultAllocator); - assert(!rc.isInitialized); - assert(rc.allocator is defaultAllocator); -} - -@nogc @system 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); -} - -@nogc @system 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); -} - -@nogc nothrow pure @safe 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); -} -do -{ - 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. - * 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); -} -do -{ - 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); -} - -@nogc @system 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); - } -} - -@nogc @system unittest -{ - auto rc = defaultAllocator.refCounted!(int[])(5); - assert(rc.length == 5); -} - -@nogc @system unittest -{ - auto p1 = defaultAllocator.make!int(5); - auto p2 = p1; - auto rc = RefCounted!int(p1, defaultAllocator); - assert(rc.get() is p2); -} - -@nogc @system unittest -{ - size_t destroyed; - { - auto rc = defaultAllocator.refCounted!WithDtor(destroyed); - } - assert(destroyed == 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 - { - assert(allocator !is null); - } - do - { - 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 -{ - assert(allocator !is null); -} -do -{ - 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. - * 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); -} -do -{ - auto payload = allocator.resize!(ElementType!T)(null, size); - return Unique!T(payload, allocator); -} - -@nogc nothrow pure @safe unittest -{ - static assert(is(typeof(defaultAllocator.unique!B(5)))); - static assert(is(typeof(defaultAllocator.unique!(int[])(5)))); -} - -@nogc nothrow pure @system unittest -{ - auto s = defaultAllocator.unique!int(5); - assert(*s == 5); - - s = null; - assert(s is null); -} - -@nogc nothrow pure @system unittest -{ - auto s = defaultAllocator.unique!int(5); - assert(*s == 5); - - s = defaultAllocator.unique!int(4); - assert(*s == 4); -} - -@nogc nothrow pure @system unittest -{ - auto p1 = defaultAllocator.make!int(5); - auto p2 = p1; - - auto rc = Unique!int(p1, defaultAllocator); - assert(rc.get() is p2); -} - -@nogc nothrow pure @system unittest -{ - auto rc = Unique!int(defaultAllocator); - assert(rc.allocator is defaultAllocator); -} diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d index 2296eac..2afdfe8 100644 --- a/source/tanya/net/ip.d +++ b/source/tanya/net/ip.d @@ -21,6 +21,7 @@ import tanya.container.string; import tanya.conv; import tanya.encoding.ascii; import tanya.format; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.net.iface; diff --git a/source/tanya/range/adapter.d b/source/tanya/range/adapter.d index 13f4c1e..bc20e10 100644 --- a/source/tanya/range/adapter.d +++ b/source/tanya/range/adapter.d @@ -15,7 +15,7 @@ module tanya.range.adapter; import tanya.algorithm.mutation; -import tanya.functional; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.range; diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d index ae604e9..0b192b0 100644 --- a/source/tanya/range/primitive.d +++ b/source/tanya/range/primitive.d @@ -15,7 +15,7 @@ module tanya.range.primitive; import tanya.algorithm.comparison; -import tanya.algorithm.mutation; +import tanya.memory.lifecycle; import tanya.meta.trait; import tanya.meta.transform; import tanya.range.array; diff --git a/source/tanya/test/assertion.d b/source/tanya/test/assertion.d deleted file mode 100644 index 10105d7..0000000 --- a/source/tanya/test/assertion.d +++ /dev/null @@ -1,105 +0,0 @@ -/* 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/. */ - -/** - * Additional assertions. - * - * This module provides functions that assert whether a given expression - * satisfies some complex condition, that can't be tested with - * $(D_KEYWORD assert) in a single line. Internally all the functions - * just evaluate the expression and call $(D_KEYWORD assert). - * - * The functions can cause segmentation fault if the module is compiled - * in production mode and the condition fails. - * - * Copyright: Eugene Wissner 2017-2018. - * 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/source/tanya/test/assertion.d, - * tanya/test/assertion.d) - */ -module tanya.test.assertion; - -import tanya.memory; -import tanya.meta.trait; - -/** - * Asserts whether the function $(D_PARAM expr) throws an exception of type - * $(D_PARAM E). If it does, the exception is catched and properly destroyed. - * If it doesn't, an assertion error is thrown. If the exception doesn't match - * $(D_PARAM E) type, it isn't catched and escapes. - * - * Params: - * E = Expected exception type. - * T = Throwing function type. - * Args = Argument types of the throwing function. - * expr = Throwing function. - * args = Arguments for $(D_PARAM expr). - */ -void assertThrown(E : Exception, T, Args...)(T expr, auto ref Args args) -if (isSomeFunction!T) -{ - try - { - cast(void) expr(args); - assert(false, "Expected exception not thrown"); - } - catch (E exception) - { - defaultAllocator.dispose(exception); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - // If you want to test that an expression throws, you can wrap it into an - // arrow function. - static struct CtorThrows - { - this(int i) @nogc pure @safe - { - throw defaultAllocator.make!Exception(); - } - } - assertThrown!Exception(() => CtorThrows(8)); -} - -/** - * Asserts that the function $(D_PARAM expr) doesn't throw. - * - * If it does, the thrown exception is catched, properly destroyed and an - * assertion error is thrown instead. - * - * Params: - * T = Tested function type. - * Args = Argument types of $(D_PARAM expr). - * expr = Tested function. - * args = Arguments for $(D_PARAM expr). - */ -void assertNotThrown(T, Args...)(T expr, auto ref Args args) -if (isSomeFunction!T) -{ - try - { - cast(void) expr(args); - } - catch (Exception exception) - { - defaultAllocator.dispose(exception); - assert(false, "Unexpected exception thrown"); - } -} - -/// -@nogc nothrow pure @safe unittest -{ - // If you want to test that an expression doesn't throw, you can wrap it - // into an arrow function. - static struct S - { - } - assertNotThrown(() => S()); -} diff --git a/source/tanya/test/package.d b/source/tanya/test/package.d deleted file mode 100644 index ab6f861..0000000 --- a/source/tanya/test/package.d +++ /dev/null @@ -1,18 +0,0 @@ -/* 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/. */ - -/** - * Test suite for $(D_KEYWORD unittest)-blocks. - * - * Copyright: Eugene Wissner 2017-2018. - * 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/source/tanya/test/package.d, - * tanya/test/package.d) - */ -module tanya.test; - -public import tanya.test.assertion; -public import tanya.test.stub; diff --git a/source/tanya/test/stub.d b/source/tanya/test/stub.d deleted file mode 100644 index e1f8dcb..0000000 --- a/source/tanya/test/stub.d +++ /dev/null @@ -1,373 +0,0 @@ -/* 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/. */ - -/** - * Range and generic type generators. - * - * Copyright: Eugene Wissner 2018. - * 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/source/tanya/test/stub.d, - * tanya/test/stub.d) - */ -module tanya.test.stub; - -/** - * Attribute signalizing that the generated range should contain the given - * number of elements. - * - * $(D_PSYMBOL Count) should be always specified with some value and not as a - * type, so $(D_INLINECODE Count(1)) instead just $(D_INLINECODE Count), - * otherwise you can just omit $(D_PSYMBOL Count) and it will default to 0. - * - * $(D_PSYMBOL Count) doesn't generate `.length` property - use - * $(D_PSYMBOL Length) for that. - * - * If neither $(D_PSYMBOL Length) nor $(D_PSYMBOL Infinite) is given, - * $(D_ILNINECODE Count(0)) is assumed. - * - * This attribute conflicts with $(D_PSYMBOL Infinite) and $(D_PSYMBOL Length). - */ -struct Count -{ - /// Original range length. - size_t count = 0; - - @disable this(); - - /** - * Constructs the attribute with the given length. - * - * Params: - * count = Original range length. - */ - this(size_t count) @nogc nothrow pure @safe - { - this.count = count; - } -} - -/** - * Attribute signalizing that the generated range should be infinite. - * - * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Length). - */ -struct Infinite -{ -} - -/** - * Generates `.length` property for the range. - * - * The length of the range can be specified as a constructor argument, - * otherwise it is 0. - * - * This attribute conflicts with $(D_PSYMBOL Count) and $(D_PSYMBOL Infinite). - */ -struct Length -{ - /// Original range length. - size_t length = 0; -} - -/** - * Attribute signalizing that the generated range should return values by - * reference. - * - * This atribute affects the return values of `.front`, `.back` and `[]`. - */ -struct WithLvalueElements -{ -} - -/** - * Generates an input range. - * - * Params: - * E = Element type. - */ -mixin template InputRangeStub(E = int) -{ - import tanya.meta.metafunction : Alias; - import tanya.meta.trait : evalUDA, getUDAs, hasUDA; - - /* - * Aliases for the attribute lookups to access them faster - */ - private enum bool infinite = hasUDA!(typeof(this), Infinite); - private enum bool withLvalueElements = hasUDA!(typeof(this), - WithLvalueElements); - private alias Count = getUDAs!(typeof(this), .Count); - private alias Length = getUDAs!(typeof(this), .Length); - - static if (Count.length != 0) - { - private enum size_t count = Count[0].count; - - static assert (!infinite, - "Range cannot have count and be infinite at the same time"); - static assert (Length.length == 0, - "Range cannot have count and length at the same time"); - } - else static if (Length.length != 0) - { - private enum size_t count = evalUDA!(Length[0]).length; - - static assert (!infinite, - "Range cannot have length and be infinite at the same time"); - } - else static if (!infinite) - { - private enum size_t count = 0; - } - - /* - * Member generation - */ - static if (infinite) - { - enum bool empty = false; - } - else - { - private size_t length_ = count; - - @property bool empty() const @nogc nothrow pure @safe - { - return this.length_ == 0; - } - } - - static if (withLvalueElements) - { - private E* element; // Pointer to enable range copying in save() - } - - void popFront() @nogc nothrow pure @safe - in (!empty) - { - static if (!infinite) - { - --this.length_; - } - } - - static if (withLvalueElements) - { - ref E front() @nogc nothrow pure @safe - in (!empty) - { - return *this.element; - } - } - else - { - E front() @nogc nothrow pure @safe - in (!empty) - { - return E.init; - } - } - - static if (Length.length != 0) - { - size_t length() const @nogc nothrow pure @safe - { - return this.length_; - } - } -} - -/** - * Generates a forward range. - * - * This mixin includes input range primitives as well, but can be combined with - * $(D_PSYMBOL RandomAccessRangeStub). - * - * Params: - * E = Element type. - */ -mixin template ForwardRangeStub(E = int) -{ - static if (!is(typeof(this.InputRangeMixin) == void)) - { - mixin InputRangeStub!E InputRangeMixin; - } - - auto save() @nogc nothrow pure @safe - { - return this; - } -} - -/** - * Generates a bidirectional range. - * - * This mixin includes forward range primitives as well, but can be combined with - * $(D_PSYMBOL RandomAccessRangeStub). - * - * Params: - * E = Element type. - */ -mixin template BidirectionalRangeStub(E = int) -{ - mixin ForwardRangeStub!E; - - void popBack() @nogc nothrow pure @safe - in (!empty) - { - static if (!infinite) - { - --this.length_; - } - } - - static if (withLvalueElements) - { - ref E back() @nogc nothrow pure @safe - in (!empty) - { - return *this.element; - } - } - else - { - E back() @nogc nothrow pure @safe - in (!empty) - { - return E.init; - } - } -} - -/** - * Generates a random-access range. - * - * This mixin includes input range primitives as well, but can be combined with - * $(D_PSYMBOL ForwardRangeStub) or $(D_PSYMBOL BidirectionalRangeStub). - * - * Note that a random-access range also requires $(D_PSYMBOL Length) or - * $(D_PARAM Infinite) by definition. - * - * Params: - * E = Element type. - */ -mixin template RandomAccessRangeStub(E = int) -{ - static if (!is(typeof(this.InputRangeMixin) == void)) - { - mixin InputRangeStub!E InputRangeMixin; - } - - static if (withLvalueElements) - { - ref E opIndex(size_t) @nogc nothrow pure @safe - { - return *this.element; - } - } - else - { - E opIndex(size_t) @nogc nothrow pure @safe - { - return E.init; - } - } -} - -/** - * Struct with a disabled postblit constructor. - * - * $(D_PSYMBOL NonCopyable) can be used as an attribute for - * $(D_PSYMBOL StructStub) or as a standalone type. - */ -struct NonCopyable -{ - @disable this(this); -} - -/** - * Struct with an elaborate destructor. - * - * $(D_PSYMBOL WithDtor) can be used as an attribute for - * $(D_PSYMBOL StructStub) or as a standalone type. - * - * When used as a standalone object the constructor of $(D_PSYMBOL WithDtor) - * accepts an additional `counter` argument, which is incremented by the - * destructor. $(D_PSYMBOL WithDtor) stores a pointer to the passed variable, - * so the variable can be investigated after the struct isn't available - * anymore. - */ -struct WithDtor -{ - size_t* counter; - - this(ref size_t counter) @nogc nothrow pure @trusted - { - this.counter = &counter; - } - - ~this() @nogc nothrow pure @safe - { - if (this.counter !is null) - { - ++*this.counter; - } - } -} - -/** - * Struct supporting hashing. - * - * $(D_PSYMBOL Hashable) can be used as an attribute for - * $(D_PSYMBOL StructStub) or as a standalone type. - * - * The constructor accepts an additional parameter, which is returned by the - * `toHash()`-function. `0U` is returned if no hash value is given. - */ -struct Hashable -{ - size_t hash; - - size_t toHash() const @nogc nothrow pure @safe - { - return this.hash; - } -} - -/** - * Generates a $(D_KEYWORD struct) with common functionality. - * - * To specify the needed functionality use user-defined attributes on the - * $(D_KEYWORD struct) $(D_PSYMBOL StructStub) is mixed in. - * - * Supported attributes are: $(D_PSYMBOL NonCopyable), $(D_PSYMBOL Hashable), - * $(D_PSYMBOL WithDtor). - */ -mixin template StructStub() -{ - import tanya.meta.trait : evalUDA, getUDAs, hasUDA; - - static if (hasUDA!(typeof(this), NonCopyable)) - { - @disable this(this); - } - - private alias Hashable = getUDAs!(typeof(this), .Hashable); - static if (Hashable.length > 0) - { - size_t toHash() const @nogc nothrow pure @safe - { - return evalUDA!(Hashable[0]).hash; - } - } - - static if (hasUDA!(typeof(this), WithDtor)) - { - ~this() @nogc nothrow pure @safe - { - } - } -} diff --git a/source/tanya/typecons.d b/source/tanya/typecons.d index 1407c62..fa09831 100644 --- a/source/tanya/typecons.d +++ b/source/tanya/typecons.d @@ -17,10 +17,8 @@ */ module tanya.typecons; -import tanya.algorithm.mutation; -import tanya.conv; import tanya.format; -import tanya.functional; +import tanya.memory.lifecycle; import tanya.meta.metafunction; import tanya.meta.trait; version (unittest) import tanya.test.stub; -- cgit v1.2.3