From 5ab99cf8873b130284336c52551ccede28f6cb2e Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 17 Mar 2019 10:56:44 +0100 Subject: [PATCH] Move memory functions into memory.lifecycle - move - moveEmplace - forward - emplace - swap --- .travis.yml | 6 +- appveyor.yml | 1 + dub.json | 6 +- encoding/dub.json | 9 +- encoding/{source => }/tanya/encoding/ascii.d | 2 +- .../{source => }/tanya/encoding/package.d | 2 +- memory/dub.json | 18 + {source => memory}/tanya/memory/allocator.d | 2 +- memory/tanya/memory/lifecycle.d | 984 ++++++++++++++++++ {source => memory}/tanya/memory/mallocator.d | 2 +- {source => memory}/tanya/memory/mmappool.d | 4 +- {source => memory}/tanya/memory/op.d | 2 +- {source => memory}/tanya/memory/package.d | 87 +- {source => memory}/tanya/memory/smartref.d | 37 +- {source => memory}/tanya/test/assertion.d | 0 {source => memory}/tanya/test/package.d | 0 {source => memory}/tanya/test/stub.d | 0 meta/dub.json | 9 +- meta/{source => }/tanya/meta/metafunction.d | 4 +- meta/{source => }/tanya/meta/package.d | 4 +- meta/{source => }/tanya/meta/trait.d | 56 +- meta/{source => }/tanya/meta/transform.d | 4 +- os/dub.json | 9 +- os/{source => }/tanya/os/error.d | 2 +- os/{source => }/tanya/os/package.d | 2 +- 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/lifecycle.d | 360 ------- source/tanya/net/ip.d | 1 + source/tanya/range/adapter.d | 2 +- source/tanya/range/primitive.d | 2 +- source/tanya/typecons.d | 4 +- sys/dub.json | 9 +- sys/{source => }/tanya/sys/linux/syscall.d | 2 - sys/{source => }/tanya/sys/posix/ioctl.d | 2 - sys/{source => }/tanya/sys/posix/mman.d | 2 - sys/{source => }/tanya/sys/posix/net/if_.d | 2 - sys/{source => }/tanya/sys/posix/socket.d | 2 - sys/{source => }/tanya/sys/windows/def.d | 2 - sys/{source => }/tanya/sys/windows/ifdef.d | 2 - sys/{source => }/tanya/sys/windows/iphlpapi.d | 2 - sys/{source => }/tanya/sys/windows/package.d | 2 - sys/{source => }/tanya/sys/windows/winbase.d | 2 - sys/{source => }/tanya/sys/windows/winsock2.d | 2 - 49 files changed, 1156 insertions(+), 1130 deletions(-) rename encoding/{source => }/tanya/encoding/ascii.d (99%) rename encoding/{source => }/tanya/encoding/package.d (94%) create mode 100644 memory/dub.json rename {source => memory}/tanya/memory/allocator.d (98%) create mode 100644 memory/tanya/memory/lifecycle.d rename {source => memory}/tanya/memory/mallocator.d (99%) rename {source => memory}/tanya/memory/mmappool.d (99%) rename {source => memory}/tanya/memory/op.d (99%) rename {source => memory}/tanya/memory/package.d (61%) rename {source => memory}/tanya/memory/smartref.d (96%) rename {source => memory}/tanya/test/assertion.d (100%) rename {source => memory}/tanya/test/package.d (100%) rename {source => memory}/tanya/test/stub.d (100%) rename meta/{source => }/tanya/meta/metafunction.d (99%) rename meta/{source => }/tanya/meta/package.d (92%) rename meta/{source => }/tanya/meta/trait.d (97%) rename meta/{source => }/tanya/meta/transform.d (99%) rename os/{source => }/tanya/os/error.d (99%) rename os/{source => }/tanya/os/package.d (95%) delete mode 100644 source/tanya/memory/lifecycle.d rename sys/{source => }/tanya/sys/linux/syscall.d (93%) rename sys/{source => }/tanya/sys/posix/ioctl.d (95%) rename sys/{source => }/tanya/sys/posix/mman.d (85%) rename sys/{source => }/tanya/sys/posix/net/if_.d (80%) rename sys/{source => }/tanya/sys/posix/socket.d (96%) rename sys/{source => }/tanya/sys/windows/def.d (93%) rename sys/{source => }/tanya/sys/windows/ifdef.d (79%) rename sys/{source => }/tanya/sys/windows/iphlpapi.d (86%) rename sys/{source => }/tanya/sys/windows/package.d (81%) rename sys/{source => }/tanya/sys/windows/winbase.d (91%) rename sys/{source => }/tanya/sys/windows/winsock2.d (97%) diff --git a/.travis.yml b/.travis.yml index 3cb4f7a..ee8283f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: include: - name: D-Scanner d: dmd-$LATEST - env: DSCANNER=0.6.0 + env: DSCANNER=0.7.0 os: linux - name: DDoc d: dmd-$LATEST @@ -32,7 +32,7 @@ matrix: allow_failures: - name: D-Scanner d: dmd-$LATEST - env: DSCANNER=0.6.0 + env: DSCANNER=0.7.0 os: linux addons: @@ -53,12 +53,14 @@ script: dub build :sys -b ddox --compiler=$DC; dub build :os -b ddox --compiler=$DC; dub build :encoding -b ddox --compiler=$DC; + dub build :memory -b ddox --compiler=$DC; dub build -b ddox --compiler=$DC; elif [ -z "$DSCANNER" ]; then dub test :meta -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; dub test :sys -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; dub test :os -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; dub test :encoding -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; + dub test :memory -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC; else dub fetch dscanner --version=$DSCANNER; diff --git a/appveyor.yml b/appveyor.yml index fd4db00..dc0e5e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,4 +61,5 @@ test_script: - dub test :sys -b unittest --arch=%Darch% --compiler=%DC% - dub test :os -b unittest --arch=%Darch% --compiler=%DC% - dub test :encoding -b unittest --arch=%Darch% --compiler=%DC% + - dub test :memory -b unittest --arch=%Darch% --compiler=%DC% - dub test -b unittest --arch=%Darch% --compiler=%DC% diff --git a/dub.json b/dub.json index 823f97f..3fd1ec6 100644 --- a/dub.json +++ b/dub.json @@ -13,7 +13,8 @@ "tanya:meta": "*", "tanya:sys": "*", "tanya:os": "*", - "tanya:encoding": "*" + "tanya:encoding": "*", + "tanya:memory": "*" }, "dependencies-linux": { @@ -24,7 +25,8 @@ "./meta", "./sys", "./os", - "./encoding" + "./encoding", + "./memory" ], "configurations": [ diff --git a/encoding/dub.json b/encoding/dub.json index 9c50acd..00ef6de 100644 --- a/encoding/dub.json +++ b/encoding/dub.json @@ -5,5 +5,12 @@ "dependencies": { "tanya:meta": "*" - } + }, + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] } diff --git a/encoding/source/tanya/encoding/ascii.d b/encoding/tanya/encoding/ascii.d similarity index 99% rename from encoding/source/tanya/encoding/ascii.d rename to encoding/tanya/encoding/ascii.d index c664861..6498547 100644 --- a/encoding/source/tanya/encoding/ascii.d +++ b/encoding/tanya/encoding/ascii.d @@ -12,7 +12,7 @@ * 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/encoding/ascii.d, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/encoding/tanya/encoding/ascii.d, * tanya/encoding/ascii.d) */ module tanya.encoding.ascii; diff --git a/encoding/source/tanya/encoding/package.d b/encoding/tanya/encoding/package.d similarity index 94% rename from encoding/source/tanya/encoding/package.d rename to encoding/tanya/encoding/package.d index e9912ee..9ec74d0 100644 --- a/encoding/source/tanya/encoding/package.d +++ b/encoding/tanya/encoding/package.d @@ -9,7 +9,7 @@ * 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/encoding/package.d, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/encoding/tanya/encoding/package.d, * tanya/encoding/package.d) */ module tanya.encoding; diff --git a/memory/dub.json b/memory/dub.json new file mode 100644 index 0000000..7339127 --- /dev/null +++ b/memory/dub.json @@ -0,0 +1,18 @@ +{ + "name": "memory", + "description": "Tools for manual memory management (allocators, smart pointers)", + "targetType": "library", + + "dependencies": { + "tanya:meta": "*", + "tanya:os": "*", + "tanya:sys": "*" + }, + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] +} diff --git a/source/tanya/memory/allocator.d b/memory/tanya/memory/allocator.d similarity index 98% rename from source/tanya/memory/allocator.d rename to memory/tanya/memory/allocator.d index 3d05a37..e161612 100644 --- a/source/tanya/memory/allocator.d +++ b/memory/tanya/memory/allocator.d @@ -12,7 +12,7 @@ * 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, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/allocator.d, * tanya/memory/allocator.d) */ module tanya.memory.allocator; diff --git a/memory/tanya/memory/lifecycle.d b/memory/tanya/memory/lifecycle.d new file mode 100644 index 0000000..35d251b --- /dev/null +++ b/memory/tanya/memory/lifecycle.d @@ -0,0 +1,984 @@ +/* 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/memory/tanya/memory/lifecycle.d, + * tanya/memory/lifecycle.d) + */ +module tanya.memory.lifecycle; + +import tanya.memory : defaultAllocator; +import tanya.memory.allocator; +import tanya.meta.trait; +import tanya.meta.metafunction; +version (unittest) import tanya.test.stub; + +/** + * 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.memory) void[] finalize(T)(ref T* p) +{ + if (p is null) + { + return null; + } + static if (hasElaborateDestructor!T) + { + destroy(*p); + } + return (cast(void*) p)[0 .. T.sizeof]; +} + +package(tanya.memory) void[] finalize(T)(ref T p) +if (isPolymorphicType!T) +{ + if (p is null) + { + return null; + } + static if (is(T == interface)) + { + version(Windows) + { + import core.sys.windows.unknwn : IUnknown; + static assert(!is(T : IUnknown), "COM interfaces can't be destroyed in " + ~ __PRETTY_FUNCTION__); + } + auto ob = cast(Object) p; + } + else + { + alias ob = p; + } + auto ptr = cast(void*) ob; + auto support = ptr[0 .. typeid(ob).initializer.length]; + + auto ppv = cast(void**) ptr; + if (!*ppv) + { + return null; + } + auto pc = cast(ClassInfo*) *ppv; + scope (exit) + { + *ppv = null; + } + + auto c = *pc; + do + { + // Assume the destructor is @nogc. Leave it nothrow since the destructor + // shouldn't throw and if it does, it is an error anyway. + if (c.destructor) + { + alias DtorType = void function(Object) pure nothrow @safe @nogc; + (cast(DtorType) c.destructor)(ob); + } + } + while ((c = c.base) !is null); + + if (ppv[1]) // if monitor is not null + { + _d_monitordelete(cast(Object) ptr, true); + } + return support; +} + +package(tanya.memory) void[] finalize(T)(ref T[] p) +{ + destroyAllImpl!(T[], T)(p); + return p; +} + +package(tanya) void destroyAllImpl(R, E)(R p) +{ + static if (hasElaborateDestructor!E) + { + foreach (ref e; p) + { + destroy(e); + } + } +} + +/** + * Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T). + * It is assumed the respective entities had been allocated with the same + * allocator. + * + * Params: + * T = Type of $(D_PARAM p). + * allocator = Allocator the $(D_PARAM p) was allocated with. + * p = Object or array to be destroyed. + */ +void dispose(T)(shared Allocator allocator, auto ref T p) +{ + () @trusted { allocator.deallocate(finalize(p)); }(); + p = null; +} + +@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 (!isPolymorphicType!T && !isAssociativeArray!T && !isArray!T) +in (allocator !is null) +{ + auto mem = (() @trusted => allocator.allocate(stateSize!T))(); + if (mem is null) + { + onOutOfMemoryError(); + } + scope (failure) + { + () @trusted { allocator.deallocate(mem); }(); + } + return emplace!T(mem[0 .. stateSize!T], args); +} + +/// +@nogc nothrow pure @safe unittest +{ + int* i = defaultAllocator.make!int(5); + assert(*i == 5); + defaultAllocator.dispose(i); +} + +/** + * Constructs a new array with $(D_PARAM n) elements. + * + * Params: + * T = Array type. + * E = Array element type. + * allocator = Allocator. + * n = Array size. + * + * Returns: Newly created array. + * + * Precondition: $(D_INLINECODE allocator !is null + * && n <= size_t.max / E.sizeof) + */ +T make(T : E[], E)(shared Allocator allocator, size_t n) +in (allocator !is null) +in (n <= size_t.max / E.sizeof) +{ + auto ret = allocator.resize!E(null, n); + + static if (hasElaborateDestructor!E) + { + for (auto range = ret; range.length != 0; range = range[1 .. $]) + { + emplace!E(cast(void[]) range[0 .. 1], E.init); + } + } + else + { + ret[] = E.init; + } + + return ret; +} + +/// +@nogc nothrow pure @safe unittest +{ + int[] i = defaultAllocator.make!(int[])(2); + assert(i.length == 2); + assert(i[0] == int.init && i[1] == int.init); + defaultAllocator.dispose(i); +} + +/** + * Constructs a new object of type $(D_PARAM T) in $(D_PARAM memory) with the + * given arguments. + * + * If $(D_PARAM T) is a $(D_KEYWORD class), emplace returns a class reference + * of type $(D_PARAM T), otherwise a pointer to the constructed object is + * returned. + * + * If $(D_PARAM T) is a nested class inside another class, $(D_PARAM outer) + * should be an instance of the outer class. + * + * $(D_PARAM args) are arguments for the constructor of $(D_PARAM T). If + * $(D_PARAM T) isn't an aggregate type and doesn't have a constructor, + * $(D_PARAM memory) can be initialized to `args[0]` if `Args.length == 1`, + * `Args[0]` should be implicitly convertible to $(D_PARAM T) then. + * + * Params: + * T = Constructed type. + * U = Type of the outer class if $(D_PARAM T) is a nested class. + * Args = Types of the constructor arguments if $(D_PARAM T) has a constructor + * or the type of the initial value. + * outer = Outer class instance if $(D_PARAM T) is a nested class. + * args = Constructor arguments if $(D_PARAM T) has a constructor or the + * initial value. + * + * Returns: New instance of type $(D_PARAM T) constructed in $(D_PARAM memory). + * + * Precondition: `memory.length == stateSize!T`. + * Postcondition: $(D_PARAM memory) and the result point to the same memory. + */ +T emplace(T, U, Args...)(void[] memory, U outer, auto ref Args args) +if (!isAbstractClass!T && isInnerClass!T && is(typeof(T.outer) == U)) +in (memory.length >= stateSize!T) +out (result; memory.ptr is (() @trusted => cast(void*) result)()) +{ + import tanya.memory.op : copy; + + copy(typeid(T).initializer, memory); + + auto result = (() @trusted => cast(T) memory.ptr)(); + result.outer = outer; + + static if (is(typeof(result.__ctor(args)))) + { + result.__ctor(args); + } + + return result; +} + +/// ditto +T emplace(T, Args...)(void[] memory, auto ref Args args) +if (is(T == class) && !isAbstractClass!T && !isInnerClass!T) +in (memory.length == stateSize!T) +out (result; memory.ptr is (() @trusted => cast(void*) result)()) +{ + import tanya.memory.op : copy; + + copy(typeid(T).initializer, memory); + + auto result = (() @trusted => cast(T) memory.ptr)(); + static if (is(typeof(result.__ctor(args)))) + { + result.__ctor(args); + } + return result; +} + +/// +@nogc nothrow pure @safe unittest +{ + class C + { + int i = 5; + class Inner + { + int i; + + this(int param) pure nothrow @safe @nogc + { + this.i = param; + } + } + } + ubyte[stateSize!C] memory1; + ubyte[stateSize!(C.Inner)] memory2; + + auto c = emplace!C(memory1); + assert(c.i == 5); + + auto inner = emplace!(C.Inner)(memory2, c, 8); + assert(c.i == 5); + assert(inner.i == 8); + assert(inner.outer is c); +} + +/// ditto +T* emplace(T, Args...)(void[] memory, auto ref Args args) +if (!isAggregateType!T && (Args.length <= 1)) +in (memory.length >= T.sizeof) +out (result; memory.ptr is result) +{ + auto result = (() @trusted => cast(T*) memory.ptr)(); + static if (Args.length == 1) + { + *result = T(args[0]); + } + else + { + *result = T.init; + } + return result; +} + +private void initializeOne(T)(ref void[] memory, ref T* result) @trusted +{ + import tanya.memory.op : copy, fill; + + static if (!hasElaborateAssign!T && isAssignable!T) + { + *result = T.init; + } + else static if (__VERSION__ >= 2083 // __traits(isZeroInit) available. + && __traits(isZeroInit, T)) + { + memory.ptr[0 .. T.sizeof].fill!0; + } + else + { + static immutable T init = T.init; + copy((&init)[0 .. 1], memory); + } +} + +/// ditto +T* emplace(T, Args...)(void[] memory, auto ref Args args) +if (!isPolymorphicType!T && isAggregateType!T) +in (memory.length >= T.sizeof) +out (result; memory.ptr is result) +{ + auto result = (() @trusted => cast(T*) memory.ptr)(); + + static if (Args.length == 0) + { + static assert(is(typeof({ static T t; })), + "Default constructor is disabled"); + initializeOne(memory, result); + } + else static if (is(typeof(result.__ctor(args)))) + { + initializeOne(memory, result); + result.__ctor(args); + } + else static if (Args.length == 1 && is(typeof({ T t = args[0]; }))) + { + import tanya.memory.op : copy; + + ((ref arg) @trusted => + copy((cast(void*) &arg)[0 .. T.sizeof], memory))(args[0]); + static if (hasElaborateCopyConstructor!T) + { + result.__postblit(); + } + } + else static if (is(typeof({ T t = T(args); }))) + { + auto init = T(args); + (() @trusted => moveEmplace(init, *result))(); + } + else + { + static assert(false, + "Unable to construct value with the given arguments"); + } + return result; +} + +/// +@nogc nothrow pure @safe unittest +{ + ubyte[4] memory; + + auto i = emplace!int(memory); + static assert(is(typeof(i) == int*)); + assert(*i == 0); + + i = emplace!int(memory, 5); + assert(*i == 5); + + static struct S + { + int i; + @disable this(); + @disable this(this); + this(int i) @nogc nothrow pure @safe + { + this.i = i; + } + } + auto s = emplace!S(memory, 8); + static assert(is(typeof(s) == S*)); + assert(s.i == 8); +} + +// 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); +} + +private void deinitialize(bool zero, T)(ref T value) +{ + static if (is(T == U[S], U, size_t S)) + { + foreach (ref e; value) + { + deinitialize!zero(e); + } + } + else + { + import tanya.memory.op : copy, fill; + + static if (isNested!T) + { + // Don't override the context pointer. + enum size_t size = T.sizeof - (void*).sizeof; + } + else + { + enum size_t size = T.sizeof; + } + static if (zero) + { + fill!0((cast(void*) &value)[0 .. size]); + } + else + { + copy(typeid(T).initializer()[0 .. size], (&value)[0 .. 1]); + } + } +} + +/** + * Moves $(D_PARAM source) into $(D_PARAM target) assuming that + * $(D_PARAM target) isn't initialized. + * + * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places + * the $(D_PARAM source) into a valid but unspecified state, which means that + * after moving $(D_PARAM source) can be destroyed or assigned a new value, but + * accessing it yields an unspecified value. No postblits or destructors are + * called. If the $(D_PARAM target) should be destroyed before, use + * $(D_PSYMBOL move). + * + * $(D_PARAM source) and $(D_PARAM target) must be different objects. + * + * Params: + * T = Object type. + * source = Source object. + * target = Target object. + * + * See_Also: $(D_PSYMBOL move), + * $(D_PSYMBOL hasElaborateCopyConstructor), + * $(D_PSYMBOL hasElaborateDestructor). + * + * Precondition: `&source !is &target`. + */ +void moveEmplace(T)(ref T source, ref T target) @system +in +{ + assert(&source !is &target, "Source and target must be different"); +} +do +{ + static if (is(T == struct) || isStaticArray!T) + { + import tanya.memory.op : copy; + + copy((&source)[0 .. 1], (&target)[0 .. 1]); + + static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) + { + static if (__VERSION__ >= 2083) // __traits(isZeroInit) available. + { + deinitialize!(__traits(isZeroInit, T))(source); + } + else + { + if (typeid(T).initializer().ptr is null) + { + deinitialize!true(source); + } + else + { + deinitialize!false(source); + } + } + } + } + else + { + target = source; + } +} + +/// +@nogc nothrow pure @system unittest +{ + static struct S + { + int member = 5; + + this(this) @nogc nothrow pure @safe + { + assert(false); + } + } + S source, target = void; + moveEmplace(source, target); + assert(target.member == 5); + + int x1 = 5, x2; + moveEmplace(x1, x2); + assert(x2 == 5); +} + +// 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); +} + +/** + * Forwards its argument list preserving $(D_KEYWORD ref) and $(D_KEYWORD out) + * storage classes. + * + * $(D_PSYMBOL forward) accepts a list of variables or literals. It returns an + * argument list of the same length that can be for example passed to a + * function accepting the arguments of this type. + * + * Params: + * args = Argument list. + * + * Returns: $(D_PARAM args) with their original storage classes. + */ +template forward(args...) +{ + static if (args.length == 0) + { + alias forward = AliasSeq!(); + } + else static if (__traits(isRef, args[0]) || __traits(isOut, args[0])) + { + static if (args.length == 1) + { + alias forward = args[0]; + } + else + { + alias forward = AliasSeq!(args[0], forward!(args[1 .. $])); + } + } + else + { + @property auto forwardOne() + { + return move(args[0]); + } + static if (args.length == 1) + { + alias forward = forwardOne; + } + else + { + alias forward = AliasSeq!(forwardOne, forward!(args[1 .. $])); + } + } +} + +/// +@nogc nothrow pure @safe unittest +{ + static assert(is(typeof((int i) { int v = forward!i; }))); + static assert(is(typeof((ref int i) { int v = forward!i; }))); + static assert(is(typeof({ + void f(int i, ref int j, out int k) + { + f(forward!(i, j, k)); + } + }))); +} diff --git a/source/tanya/memory/mallocator.d b/memory/tanya/memory/mallocator.d similarity index 99% rename from source/tanya/memory/mallocator.d rename to memory/tanya/memory/mallocator.d index fdf7ede..858ad40 100644 --- a/source/tanya/memory/mallocator.d +++ b/memory/tanya/memory/mallocator.d @@ -10,7 +10,7 @@ * 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, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/mallocator.d, * tanya/memory/mallocator.d) */ module tanya.memory.mallocator; diff --git a/source/tanya/memory/mmappool.d b/memory/tanya/memory/mmappool.d similarity index 99% rename from source/tanya/memory/mmappool.d rename to memory/tanya/memory/mmappool.d index 5c42241..1bc401b 100644 --- a/source/tanya/memory/mmappool.d +++ b/memory/tanya/memory/mmappool.d @@ -9,7 +9,7 @@ * 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, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/mmappool.d, * tanya/memory/mmappool.d) */ module tanya.memory.mmappool; @@ -398,7 +398,7 @@ final class MmapPool : Allocator } if (p !is null) { - copy(p[0 .. min(p.length, size)], reallocP); + copy(p[0 .. p.length < size ? p.length : size], reallocP); deallocate(p); } p = reallocP; diff --git a/source/tanya/memory/op.d b/memory/tanya/memory/op.d similarity index 99% rename from source/tanya/memory/op.d rename to memory/tanya/memory/op.d index 6005329..d77f3bb 100644 --- a/source/tanya/memory/op.d +++ b/memory/tanya/memory/op.d @@ -9,7 +9,7 @@ * 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, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/tanya/memory/op.d, * tanya/memory/op.d) */ module tanya.memory.op; diff --git a/source/tanya/memory/package.d b/memory/tanya/memory/package.d similarity index 61% rename from source/tanya/memory/package.d rename to memory/tanya/memory/package.d index 8526b5a..98aec48 100644 --- a/source/tanya/memory/package.d +++ b/memory/tanya/memory/package.d @@ -9,15 +9,16 @@ * 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, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/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; +deprecated("Use tanya.meta.trait.stateSize instead") +public import tanya.meta.trait : stateSize; /** * The mixin generates common methods for classes and structs using @@ -37,11 +38,7 @@ mixin template DefaultAllocator() * Precondition: $(D_INLINECODE allocator_ !is null) */ this(shared Allocator allocator) @nogc nothrow pure @safe - in - { - assert(allocator !is null); - } - do + in (allocator !is null) { this.allocator_ = allocator; } @@ -55,11 +52,7 @@ mixin template DefaultAllocator() * Postcondition: $(D_INLINECODE allocator !is null) */ @property shared(Allocator) allocator() @nogc nothrow pure @safe - out (allocator) - { - assert(allocator !is null); - } - do + out (allocator; allocator !is null) { if (allocator_ is null) { @@ -70,11 +63,7 @@ mixin template DefaultAllocator() /// ditto @property shared(Allocator) allocator() const @nogc nothrow pure @trusted - out (allocator) - { - assert(allocator !is null); - } - do + out (allocator; allocator !is null) { if (allocator_ is null) { @@ -110,11 +99,7 @@ private shared(Allocator) getAllocatorInstance() @nogc nothrow * Postcondition: $(D_INLINECODE allocator !is null). */ @property shared(Allocator) defaultAllocator() @nogc nothrow pure @trusted -out (allocator) -{ - assert(allocator !is null); -} -do +out (allocator; allocator !is null) { return (cast(GetPureInstance!Allocator) &getAllocatorInstance)(); } @@ -128,67 +113,11 @@ do * Precondition: $(D_INLINECODE allocator !is null). */ @property void defaultAllocator(shared(Allocator) allocator) @nogc nothrow @safe -in -{ - assert(allocator !is null); -} -do +in (allocator !is null) { .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. diff --git a/source/tanya/memory/smartref.d b/memory/tanya/memory/smartref.d similarity index 96% rename from source/tanya/memory/smartref.d rename to memory/tanya/memory/smartref.d index 90271e7..1fe81f3 100644 --- a/source/tanya/memory/smartref.d +++ b/memory/tanya/memory/smartref.d @@ -18,21 +18,18 @@ * 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, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/memory/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) + static if (isPolymorphicType!T || isDynamicArray!T) { alias Payload = T; } @@ -529,23 +526,19 @@ do * * Params: * T = Array type. + * E = Array element type. * size = Array size. * allocator = Allocator. * * Returns: Newly created $(D_PSYMBOL RefCounted!T). * * Precondition: $(D_INLINECODE allocator !is null - * && size <= size_t.max / ElementType!T.sizeof) + * && size <= size_t.max / E.sizeof) */ -RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size) +RefCounted!T refCounted(T : E[], E)(shared Allocator allocator, size_t size) @trusted -if (isArray!T) -in -{ - assert(allocator !is null); - assert(size <= size_t.max / ElementType!T.sizeof); -} -do +in (allocator !is null) +in (size <= size_t.max / E.sizeof) { return RefCounted!T(allocator.make!T(size), allocator); } @@ -852,25 +845,21 @@ do * * Params: * T = Array type. + * E = Array element type. * size = Array size. * allocator = Allocator. * * Returns: Newly created $(D_PSYMBOL Unique!T). * * Precondition: $(D_INLINECODE allocator !is null - * && size <= size_t.max / ElementType!T.sizeof) + * && size <= size_t.max / E.sizeof) */ -Unique!T unique(T)(shared Allocator allocator, const size_t size) +Unique!T unique(T : E[], E)(shared Allocator allocator, size_t size) @trusted -if (isArray!T) -in +in (allocator !is null) +in (size <= size_t.max / E.sizeof) { - assert(allocator !is null); - assert(size <= size_t.max / ElementType!T.sizeof); -} -do -{ - auto payload = allocator.resize!(ElementType!T)(null, size); + auto payload = allocator.resize!E(null, size); return Unique!T(payload, allocator); } diff --git a/source/tanya/test/assertion.d b/memory/tanya/test/assertion.d similarity index 100% rename from source/tanya/test/assertion.d rename to memory/tanya/test/assertion.d diff --git a/source/tanya/test/package.d b/memory/tanya/test/package.d similarity index 100% rename from source/tanya/test/package.d rename to memory/tanya/test/package.d diff --git a/source/tanya/test/stub.d b/memory/tanya/test/stub.d similarity index 100% rename from source/tanya/test/stub.d rename to memory/tanya/test/stub.d diff --git a/meta/dub.json b/meta/dub.json index c8d2ba7..663d2fd 100644 --- a/meta/dub.json +++ b/meta/dub.json @@ -1,5 +1,12 @@ { "name": "meta", "description": "Template metaprogramming", - "targetType": "library" + "targetType": "library", + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] } diff --git a/meta/source/tanya/meta/metafunction.d b/meta/tanya/meta/metafunction.d similarity index 99% rename from meta/source/tanya/meta/metafunction.d rename to meta/tanya/meta/metafunction.d index 6da6b47..a05b5cf 100644 --- a/meta/source/tanya/meta/metafunction.d +++ b/meta/tanya/meta/metafunction.d @@ -9,11 +9,11 @@ * It contains different algorithms for iterating, searching and modifying * template arguments. * - * Copyright: Eugene Wissner 2017-2018. + * 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/meta/metafunction.d, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/metafunction.d, * tanya/meta/metafunction.d) */ module tanya.meta.metafunction; diff --git a/meta/source/tanya/meta/package.d b/meta/tanya/meta/package.d similarity index 92% rename from meta/source/tanya/meta/package.d rename to meta/tanya/meta/package.d index d93e4fa..f06a166 100644 --- a/meta/source/tanya/meta/package.d +++ b/meta/tanya/meta/package.d @@ -9,11 +9,11 @@ * to transform from one type to another. It has also different algorithms for * iterating, searching and modifying template arguments. * - * Copyright: Eugene Wissner 2017-2018. + * 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/meta/package.d, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/package.d, * tanya/meta/package.d) */ module tanya.meta; diff --git a/meta/source/tanya/meta/trait.d b/meta/tanya/meta/trait.d similarity index 97% rename from meta/source/tanya/meta/trait.d rename to meta/tanya/meta/trait.d index 69b97b9..ebea9ce 100644 --- a/meta/source/tanya/meta/trait.d +++ b/meta/tanya/meta/trait.d @@ -8,11 +8,11 @@ * Templates in this module are used to obtain type information at compile * time. * - * Copyright: Eugene Wissner 2017-2018. + * 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/meta/trait.d, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/trait.d, * tanya/meta/trait.d) */ module tanya.meta.trait; @@ -2692,6 +2692,58 @@ if (is(T == class)) static assert(classInstanceAlignment!C2 == S.alignof); } +/** + * 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); +} + /** * Tests whether $(D_INLINECODE pred(T)) can be used as condition in an * $(D_KEYWORD if)-statement or a ternary operator. diff --git a/meta/source/tanya/meta/transform.d b/meta/tanya/meta/transform.d similarity index 99% rename from meta/source/tanya/meta/transform.d rename to meta/tanya/meta/transform.d index 5697e69..083e7ee 100644 --- a/meta/source/tanya/meta/transform.d +++ b/meta/tanya/meta/transform.d @@ -9,11 +9,11 @@ * types. They take some type as argument and return a different type after * perfoming the specified transformation. * - * Copyright: Eugene Wissner 2017-2018. + * 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/meta/transform.d, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/transform.d, * tanya/meta/transform.d) */ module tanya.meta.transform; diff --git a/os/dub.json b/os/dub.json index 87df29d..647e24d 100644 --- a/os/dub.json +++ b/os/dub.json @@ -5,5 +5,12 @@ "dependencies": { "tanya:meta": "*" - } + }, + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] } diff --git a/os/source/tanya/os/error.d b/os/tanya/os/error.d similarity index 99% rename from os/source/tanya/os/error.d rename to os/tanya/os/error.d index b80404a..31c0102 100644 --- a/os/source/tanya/os/error.d +++ b/os/tanya/os/error.d @@ -9,7 +9,7 @@ * 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/os/error.d, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/os/tanya/os/error.d, * tanya/os/error.d) */ module tanya.os.error; diff --git a/os/source/tanya/os/package.d b/os/tanya/os/package.d similarity index 95% rename from os/source/tanya/os/package.d rename to os/tanya/os/package.d index c472c05..070ec44 100644 --- a/os/source/tanya/os/package.d +++ b/os/tanya/os/package.d @@ -10,7 +10,7 @@ * 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/os/package.d, + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/os/tanya/os/package.d, * tanya/os/package.d) */ module tanya.os; 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]); - } - } -} +deprecated("Use tanya.memory.lifecycle.swap instead") +alias swap = tanya.memory.lifecycle.swap; -/** - * 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]); +deprecated("Use tanya.memory.lifecycle.moveEmplace instead") +alias moveEmplace = tanya.memory.lifecycle.moveEmplace; - static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) - { - static if (__VERSION__ >= 2083) // __traits(isZeroInit) available. - { - deinitialize!(__traits(isZeroInit, T))(source); - } - else - { - if (typeid(T).initializer().ptr is null) - { - deinitialize!true(source); - } - else - { - deinitialize!false(source); - } - } - } - } - else - { - target = source; - } -} - -/// -@nogc nothrow pure @system unittest -{ - static struct S - { - int member = 5; - - this(this) @nogc nothrow pure @safe - { - assert(false); - } - } - S source, target = void; - moveEmplace(source, target); - assert(target.member == 5); - - int x1 = 5, x2; - moveEmplace(x1, x2); - assert(x2 == 5); -} - -// 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/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/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/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; diff --git a/sys/dub.json b/sys/dub.json index 8fb6d71..9201582 100644 --- a/sys/dub.json +++ b/sys/dub.json @@ -1,5 +1,12 @@ { "name": "sys", "description": "Low-level operating system bindings and definitions", - "targetType": "library" + "targetType": "library", + + "sourcePaths": [ + "." + ], + "importPaths": [ + "." + ] } diff --git a/sys/source/tanya/sys/linux/syscall.d b/sys/tanya/sys/linux/syscall.d similarity index 93% rename from sys/source/tanya/sys/linux/syscall.d rename to sys/tanya/sys/linux/syscall.d index ccc3fee..5c90ea5 100644 --- a/sys/source/tanya/sys/linux/syscall.d +++ b/sys/tanya/sys/linux/syscall.d @@ -7,8 +7,6 @@ * 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/sys/linux/syscall.d, - * tanya/sys/linux/syscall.d) */ module tanya.sys.linux.syscall; diff --git a/sys/source/tanya/sys/posix/ioctl.d b/sys/tanya/sys/posix/ioctl.d similarity index 95% rename from sys/source/tanya/sys/posix/ioctl.d rename to sys/tanya/sys/posix/ioctl.d index 86d1465..c206c89 100644 --- a/sys/source/tanya/sys/posix/ioctl.d +++ b/sys/tanya/sys/posix/ioctl.d @@ -7,8 +7,6 @@ * 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/sys/posix/ioctl.d, - * tanya/sys/posix/ioctl.d) */ module tanya.sys.posix.ioctl; diff --git a/sys/source/tanya/sys/posix/mman.d b/sys/tanya/sys/posix/mman.d similarity index 85% rename from sys/source/tanya/sys/posix/mman.d rename to sys/tanya/sys/posix/mman.d index 24a4701..21266ea 100644 --- a/sys/source/tanya/sys/posix/mman.d +++ b/sys/tanya/sys/posix/mman.d @@ -7,8 +7,6 @@ * 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/sys/posix/mman.d, - * tanya/sys/posix/mman.d) */ module tanya.sys.posix.mman; diff --git a/sys/source/tanya/sys/posix/net/if_.d b/sys/tanya/sys/posix/net/if_.d similarity index 80% rename from sys/source/tanya/sys/posix/net/if_.d rename to sys/tanya/sys/posix/net/if_.d index 1cb5b43..c9e3b86 100644 --- a/sys/source/tanya/sys/posix/net/if_.d +++ b/sys/tanya/sys/posix/net/if_.d @@ -7,8 +7,6 @@ * 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/sys/posix/net/if_.d, - * tanya/sys/posix/net/if_.d) */ module tanya.sys.posix.net.if_; diff --git a/sys/source/tanya/sys/posix/socket.d b/sys/tanya/sys/posix/socket.d similarity index 96% rename from sys/source/tanya/sys/posix/socket.d rename to sys/tanya/sys/posix/socket.d index 8924a8d..4f01a66 100644 --- a/sys/source/tanya/sys/posix/socket.d +++ b/sys/tanya/sys/posix/socket.d @@ -7,8 +7,6 @@ * 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/sys/posix/socket.d, - * tanya/sys/posix/socket.d) */ module tanya.sys.posix.socket; diff --git a/sys/source/tanya/sys/windows/def.d b/sys/tanya/sys/windows/def.d similarity index 93% rename from sys/source/tanya/sys/windows/def.d rename to sys/tanya/sys/windows/def.d index 84b4864..5b7ab59 100644 --- a/sys/source/tanya/sys/windows/def.d +++ b/sys/tanya/sys/windows/def.d @@ -20,8 +20,6 @@ * 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/sys/windows/def.d, - * tanya/sys/windows/def.d) */ module tanya.sys.windows.def; diff --git a/sys/source/tanya/sys/windows/ifdef.d b/sys/tanya/sys/windows/ifdef.d similarity index 79% rename from sys/source/tanya/sys/windows/ifdef.d rename to sys/tanya/sys/windows/ifdef.d index f091427..084b5ca 100644 --- a/sys/source/tanya/sys/windows/ifdef.d +++ b/sys/tanya/sys/windows/ifdef.d @@ -7,8 +7,6 @@ * 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/sys/windows/ifdef.d, - * tanya/sys/windows/ifdef.d) */ module tanya.sys.windows.ifdef; diff --git a/sys/source/tanya/sys/windows/iphlpapi.d b/sys/tanya/sys/windows/iphlpapi.d similarity index 86% rename from sys/source/tanya/sys/windows/iphlpapi.d rename to sys/tanya/sys/windows/iphlpapi.d index 37d0f16..450b265 100644 --- a/sys/source/tanya/sys/windows/iphlpapi.d +++ b/sys/tanya/sys/windows/iphlpapi.d @@ -7,8 +7,6 @@ * 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/sys/windows/iphlpapi.d, - * tanya/sys/windows/iphlpapi.d) */ module tanya.sys.windows.iphlpapi; diff --git a/sys/source/tanya/sys/windows/package.d b/sys/tanya/sys/windows/package.d similarity index 81% rename from sys/source/tanya/sys/windows/package.d rename to sys/tanya/sys/windows/package.d index 5e9b396..ef03e29 100644 --- a/sys/source/tanya/sys/windows/package.d +++ b/sys/tanya/sys/windows/package.d @@ -7,8 +7,6 @@ * 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/sys/windows/package.d, - * tanya/sys/windows/package.d) */ module tanya.sys.windows; diff --git a/sys/source/tanya/sys/windows/winbase.d b/sys/tanya/sys/windows/winbase.d similarity index 91% rename from sys/source/tanya/sys/windows/winbase.d rename to sys/tanya/sys/windows/winbase.d index 407b6d2..ba977eb 100644 --- a/sys/source/tanya/sys/windows/winbase.d +++ b/sys/tanya/sys/windows/winbase.d @@ -9,8 +9,6 @@ * 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/sys/windows/winbase.d, - * tanya/sys/windows/winbase.d) */ module tanya.sys.windows.winbase; diff --git a/sys/source/tanya/sys/windows/winsock2.d b/sys/tanya/sys/windows/winsock2.d similarity index 97% rename from sys/source/tanya/sys/windows/winsock2.d rename to sys/tanya/sys/windows/winsock2.d index 931e76b..1c6a452 100644 --- a/sys/source/tanya/sys/windows/winsock2.d +++ b/sys/tanya/sys/windows/winsock2.d @@ -9,8 +9,6 @@ * 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/sys/windows/winsock2.d, - * tanya/sys/windows/winsock2.d) */ module tanya.sys.windows.winsock2;