From 5d6f8e52999647fd82b93669a67a2bc1545980f5 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 10 Oct 2017 06:59:34 +0200 Subject: [PATCH] Implement pure onOutOfMemory --- README.md | 3 + source/tanya/container/array.d | 14 +- source/tanya/container/queue.d | 2 +- source/tanya/container/string.d | 140 ++++++++++--------- source/tanya/conv.d | 235 ++++++++++++++++++++++++++++++++ source/tanya/exception.d | 66 +++++++++ source/tanya/memory/package.d | 12 +- source/tanya/memory/smartref.d | 114 ++++++++-------- 8 files changed, 451 insertions(+), 135 deletions(-) create mode 100644 source/tanya/conv.d create mode 100644 source/tanya/exception.d diff --git a/README.md b/README.md index 0fe83ad..bf3fe7c 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,10 @@ Tanya consists of the following packages and (top-level) modules: * `async`: Event loop (epoll, kqueue and IOCP). * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 string, Hash set. +* `conv`: This module provides functions for converting between different +types. * `encoding`: This package provides tools to work with text encodings. +* `exception`: Common exceptions and errors. * `format`: Formatting and conversion functions. * `math`: Arbitrary precision integer and a set of functions. * `memory`: Tools for manual memory management (allocators, smart pointers). diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d index 2b6e5b6..0af61c7 100644 --- a/source/tanya/container/array.d +++ b/source/tanya/container/array.d @@ -15,11 +15,10 @@ module tanya.container.array; import core.checkedint; -import core.exception; import std.algorithm.comparison; import std.algorithm.mutation; -import std.conv; import std.meta; +import tanya.exception; import tanya.memory; import tanya.meta.trait; import tanya.meta.transform; @@ -501,7 +500,7 @@ struct Array(T) buf = allocator.allocate(byteSize); if (buf is null) { - onOutOfMemoryErrorNoGC(); + onOutOfMemoryError(); } scope (failure) { @@ -708,9 +707,12 @@ struct Array(T) size_t insertBack(R)(ref R el) @trusted if (isImplicitlyConvertible!(R, T)) { - reserve(this.length_ + 1); - emplace(this.data + this.length_, el); - ++this.length_; + this.length = this.length + 1; + scope (failure) + { + this.length = this.length - 1; + } + opIndex(this.length - 1) = el; return 1; } diff --git a/source/tanya/container/queue.d b/source/tanya/container/queue.d index e32fdd4..33144c0 100644 --- a/source/tanya/container/queue.d +++ b/source/tanya/container/queue.d @@ -14,9 +14,9 @@ */ module tanya.container.queue; -import core.exception; import std.algorithm.mutation; import tanya.container.entry; +import tanya.exception; import tanya.memory; import tanya.meta.trait; diff --git a/source/tanya/container/string.d b/source/tanya/container/string.d index 4b2732b..813fe8a 100644 --- a/source/tanya/container/string.d +++ b/source/tanya/container/string.d @@ -361,14 +361,14 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("\u10437"w); assert(s == "\u10437"); } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Отказаться от вина - в этом страшная вина."d); assert(s == "Отказаться от вина - в этом страшная вина."); @@ -392,8 +392,7 @@ struct String * * Precondition: $(D_INLINECODE allocator is null). */ - this(S)(S init, shared Allocator allocator = defaultAllocator) - nothrow @trusted @nogc + this(S)(S init, shared Allocator allocator = defaultAllocator) @trusted if (is(S == String)) { this(allocator); @@ -417,8 +416,7 @@ struct String } /// ditto - this(S)(ref S init, shared Allocator allocator = defaultAllocator) - nothrow @trusted @nogc + this(S)(ref S init, shared Allocator allocator = defaultAllocator) @trusted if (is(Unqual!S == String)) { this(allocator); @@ -428,7 +426,7 @@ struct String } /// ditto - this(shared Allocator allocator) pure nothrow @safe @nogc + this(shared Allocator allocator) @nogc nothrow pure @safe in { assert(allocator !is null); @@ -478,7 +476,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { { auto s = String(1, 'О'); @@ -494,13 +492,13 @@ struct String } } - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String(0, 'K'); assert(s.length == 0); } - this(this) @nogc nothrow @trusted + this(this) @nogc nothrow pure @trusted { auto buf = this.data[0 .. this.length_]; this.length_ = capacity_ = 0; @@ -511,13 +509,13 @@ struct String /** * Destroys the string. */ - ~this() nothrow @trusted @nogc + ~this() @nogc nothrow pure @trusted { allocator.resize(this.data[0 .. this.capacity_], 0); } private void write4Bytes(ref const dchar src) - pure nothrow @trusted @nogc + @nogc nothrow pure @trusted in { assert(capacity - length >= 4); @@ -578,7 +576,7 @@ struct String * * Throws: $(D_PSYMBOL UTFException). */ - size_t insertBack(const char chr) @trusted @nogc + size_t insertBack(const char chr) @nogc pure @trusted { if ((chr & 0x80) != 0) { @@ -593,7 +591,7 @@ struct String } /// ditto - size_t insertBack(const wchar chr) @trusted @nogc + size_t insertBack(const wchar chr) @nogc pure @trusted { reserve(length + 3); @@ -606,13 +604,13 @@ struct String } // Allocates enough space for 3-byte character. - private @safe @nogc unittest + @nogc pure @safe unittest { String s; s.insertBack('\u8100'); } - private @safe @nogc unittest + @nogc pure @safe unittest { UTFException exception; try @@ -628,7 +626,7 @@ struct String } /// ditto - size_t insertBack(const dchar chr) @trusted @nogc + size_t insertBack(const dchar chr) @nogc pure @trusted { reserve(length + dchar.sizeof); @@ -648,7 +646,7 @@ struct String } } - private @safe @nogc unittest + @nogc pure @safe unittest { UTFException exception; try @@ -835,7 +833,7 @@ struct String * Params: * size = Desired size in bytes. */ - void reserve(const size_t size) nothrow @trusted @nogc + void reserve(const size_t size) @nogc nothrow pure @trusted { if (this.capacity_ >= size) { @@ -847,7 +845,7 @@ struct String } /// - @nogc @safe unittest + @nogc pure @safe unittest { String s; assert(s.capacity == 0); @@ -871,7 +869,7 @@ struct String * Params: * size = Desired size. */ - void shrink(const size_t size) nothrow @trusted @nogc + void shrink(const size_t size) @nogc nothrow pure @trusted { if (this.capacity_ <= size) { @@ -888,7 +886,7 @@ struct String } /// - @nogc @safe unittest + @nogc pure @safe unittest { auto s = String("Die Alten lasen laut."); assert(s.capacity == 21); @@ -907,13 +905,13 @@ struct String /** * Returns: String capacity in bytes. */ - @property size_t capacity() const pure nothrow @safe @nogc + @property size_t capacity() const @nogc nothrow pure @safe { return this.capacity_; } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("In allem Schreiben ist Schamlosigkeit."); assert(s.capacity == 38); @@ -935,7 +933,7 @@ struct String */ ByCodeUnit!char opSliceAssign(R)(ByCodeUnit!R value, const size_t i, - const size_t j) @trusted + const size_t j) if (is(Unqual!R == char)) in { @@ -954,7 +952,7 @@ struct String ByCodeUnit!char opSliceAssign(const char[] value, const size_t i, const size_t j) - pure nothrow @trusted @nogc + @nogc nothrow pure @trusted in { assert(i <= j); @@ -970,7 +968,7 @@ struct String ByCodeUnit!char opSliceAssign(const char value, const size_t i, const size_t j) - pure nothrow @trusted @nogc + @nogc nothrow pure @trusted in { assert(i <= j); @@ -992,13 +990,13 @@ struct String * * Returns: The array representing the string. */ - inout(char)[] get() inout pure nothrow @trusted @nogc + inout(char)[] get() inout @nogc nothrow pure @trusted { return this.data[0 .. this.length_]; } /// - nothrow @safe @nogc unittest + @nogc nothrow pure @safe unittest { auto s = String("Char array."); assert(s.get().length == 11); @@ -1010,7 +1008,7 @@ struct String * * Returns: Null-terminated string. */ - const(char)* toStringz() nothrow @nogc + const(char)* toStringz() @nogc nothrow pure { reserve(length + 1); this.data[length] = '\0'; @@ -1018,7 +1016,7 @@ struct String } /// - @nogc unittest + @nogc pure unittest { auto s = String("C string."); assert(s.toStringz()[0] == 'C'); @@ -1028,7 +1026,7 @@ struct String /** * Returns: The number of code units that are required to encode the string. */ - @property size_t length() const pure nothrow @safe @nogc + @property size_t length() const @nogc nothrow pure @safe { return this.length_; } @@ -1037,7 +1035,7 @@ struct String alias opDollar = length; /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Piscis primuin a capite foetat."); assert(s.length == 31); @@ -1052,7 +1050,7 @@ struct String * * Precondition: $(D_INLINECODE length > pos). */ - ref inout(char) opIndex(const size_t pos) inout pure nothrow @trusted @nogc + ref inout(char) opIndex(const size_t pos) inout @nogc nothrow pure @trusted in { assert(length > pos); @@ -1063,7 +1061,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Alea iacta est."); assert(s[0] == 'A'); @@ -1074,7 +1072,7 @@ struct String * Returns: Random access range that iterates over the string by bytes, in * forward order. */ - ByCodeUnit!char opIndex() pure nothrow @trusted @nogc + ByCodeUnit!char opIndex() @nogc nothrow pure @trusted { return typeof(return)(this, this.data, this.data + length); } @@ -1086,7 +1084,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Plutarchus"); auto r = s[]; @@ -1104,22 +1102,35 @@ struct String assert(r.length == 8); } + /// + @nogc pure @safe unittest + { + auto s = const String("Was ich vermag, soll gern geschehen. Goethe"); + auto r1 = s[]; + assert(r1.front == 'W'); + + auto r2 = r1[]; + r1.popFront(); + assert(r1.front == 'a'); + assert(r2.front == 'W'); + } + /** * Returns: Forward range that iterates over the string by code points. */ - ByCodePoint!char byCodePoint() pure nothrow @trusted @nogc + ByCodePoint!char byCodePoint() @nogc nothrow pure @trusted { return typeof(return)(this, this.data, this.data + length); } /// ditto - ByCodePoint!(const char) byCodePoint() const pure nothrow @trusted @nogc + ByCodePoint!(const char) byCodePoint() const @nogc nothrow pure @trusted { return typeof(return)(this, this.data, this.data + length); } /// - @nogc @safe unittest + @nogc pure @safe unittest { auto s = String("Мне есть, что спеть, представ перед Всевышним."); auto cp = s.byCodePoint(); @@ -1139,7 +1150,7 @@ struct String } /// - @nogc @safe unittest + @nogc pure @safe unittest { auto s = const String("Высоцкий"); auto cp1 = s.byCodePoint(); @@ -1157,15 +1168,18 @@ struct String } /** - * Returns: $(D_KEYWORD true) if the string is empty. + * Returns whether the string is empty. + * + * Returns: $(D_KEYWORD true) if the string is empty, $(D_KEYWORD false) + * otherwise. */ - @property bool empty() const pure nothrow @safe @nogc + @property bool empty() const @nogc nothrow pure @safe { return length == 0; } /// - @safe @nogc unittest + @nogc pure @safe unittest { String s; assert(s.empty); @@ -1185,7 +1199,7 @@ struct String * Precondition: $(D_INLINECODE i <= j && j <= length). */ ByCodeUnit!char opSlice(const size_t i, const size_t j) - pure nothrow @trusted @nogc + @nogc nothrow pure @trusted in { assert(i <= j); @@ -1198,7 +1212,7 @@ struct String /// ditto ByCodeUnit!(const char) opSlice(const size_t i, const size_t j) - const pure nothrow @trusted @nogc + const @nogc nothrow pure @trusted in { assert(i <= j); @@ -1210,7 +1224,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Vladimir Soloviev"); auto r = s[9 .. $]; @@ -1274,7 +1288,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Черная, потом пропахшая выть!"); s = String("Как мне тебя не ласкать, не любить?"); @@ -1291,7 +1305,7 @@ struct String * * Throws: $(D_PSYMBOL UTFException). */ - ref String opAssign(S)(S that) nothrow + ref String opAssign(S)(S that) if (!isInfinite!S && isInputRange!S && isSomeChar!(ElementType!S)) @@ -1302,7 +1316,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Оловом светится лужная голь..."); s = "Грустная песня, ты - русская боль."; @@ -1347,7 +1361,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { assert(String("Голубая кофта.") < String("Синие глаза.")); assert(String("Никакой я правды") < String("милой не сказал")[]); @@ -1400,7 +1414,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { assert(String("Милая спросила:") != String("Крутит ли метель?")); assert(String("Затопить бы печку,") != String("постелить постель.")[]); @@ -1427,13 +1441,13 @@ struct String * Precondition: $(D_INLINECODE length > pos). */ ref char opIndexAssign(const char value, const size_t pos) - pure nothrow @safe @nogc + @nogc nothrow pure @safe { return opIndex(pos) = value; } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("alea iacta est."); @@ -1458,7 +1472,7 @@ struct String return opSliceAssign(value, 0, length); } - private unittest + @nogc pure @safe unittest { auto s1 = String("Buttercup"); auto s2 = String("Cap"); @@ -1467,12 +1481,12 @@ struct String } /// ditto - ByCodeUnit!char opIndexAssign(const char value) pure nothrow @safe @nogc + ByCodeUnit!char opIndexAssign(const char value) @nogc nothrow pure @safe { return opSliceAssign(value, 0, length); } - private unittest + @nogc pure @safe unittest { auto s1 = String("Wow"); s1[] = 'a'; @@ -1480,12 +1494,12 @@ struct String } /// ditto - ByCodeUnit!char opIndexAssign(const char[] value) pure nothrow @safe @nogc + ByCodeUnit!char opIndexAssign(const char[] value) @nogc nothrow pure @safe { return opSliceAssign(value, 0, length); } - private unittest + @nogc pure @safe unittest { auto s1 = String("ö"); s1[] = "oe"; @@ -1521,7 +1535,7 @@ struct String } /// - @nogc @safe unittest + @nogc pure @safe unittest { auto s = String("Из пословицы слова не выкинешь."); @@ -1575,7 +1589,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Казнить нельзя помиловать."); s.insertAfter(s[0 .. 27], ","); @@ -1604,7 +1618,7 @@ struct String } /// - @safe @nogc unittest + @nogc pure @safe unittest { auto s = String("Казнить нельзя помиловать."); s.insertBefore(s[27 .. $], ","); @@ -1619,7 +1633,7 @@ struct String } // Postblit works. -@nogc @safe unittest +@nogc pure @safe unittest { void internFunc(String arg) { diff --git a/source/tanya/conv.d b/source/tanya/conv.d new file mode 100644 index 0000000..5a5fac6 --- /dev/null +++ b/source/tanya/conv.d @@ -0,0 +1,235 @@ +/* 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 provides functions for converting between different types. + * + * Copyright: Eugene Wissner 2017. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/conv.d, + * tanya/conv.d) + */ +module tanya.conv; + +import tanya.memory; +import tanya.memory.op; +import tanya.meta.trait; + +/** + * 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 +{ + assert(memory.length >= stateSize!T); +} +out (result) +{ + assert(memory.ptr is (() @trusted => cast(void*) result)()); +} +body +{ + 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 +{ + assert(memory.length == stateSize!T); +} +out (result) +{ + assert(memory.ptr is (() @trusted => cast(void*) result)()); +}body +{ + 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 +{ + assert(memory.length >= T.sizeof); +} +out (result) +{ + assert(memory.ptr is result); +} +body +{ + auto result = (() @trusted => cast(T*) memory.ptr)(); + static if (Args.length == 1) + { + *result = T(args[0]); + } + else + { + *result = T.init; + } + return result; +} + +/// ditto +T* emplace(T, Args...)(void[] memory, auto ref Args args) +if (!isPolymorphicType!T && isAggregateType!T) +in +{ + assert(memory.length >= T.sizeof); +} +out (result) +{ + assert(memory.ptr is result); +} +body +{ + auto result = (() @trusted => cast(T*) memory.ptr)(); + static if (!hasElaborateAssign!T && isAssignable!T) + { + *result = T.init; + } + else + { + static const T init = T.init; + copy((cast(void*) &init)[0 .. T.sizeof], memory); + } + + static if (Args.length == 0) + { + static assert(is(typeof({ static T t; })), + "Default constructor is disabled"); + } + else static if (is(typeof(T(args)))) + { + *result = T(args); + } + else static if (is(typeof(result.__ctor(args)))) + { + result.__ctor(args); + } + 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)))); +} diff --git a/source/tanya/exception.d b/source/tanya/exception.d new file mode 100644 index 0000000..c329999 --- /dev/null +++ b/source/tanya/exception.d @@ -0,0 +1,66 @@ +/* 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/. */ + +/** + * Common exceptions and errors. + * + * Copyright: Eugene Wissner 2017. + * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, + * Mozilla Public License, v. 2.0). + * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) + * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/exception.d, + * tanya/exception.d) + */ +module tanya.exception; + +import tanya.conv; +import tanya.memory; + +/** + * 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); +} diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index e49aaf5..1310f37 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -14,10 +14,10 @@ */ module tanya.memory; -import core.exception; import std.algorithm.iteration; import std.algorithm.mutation; -import std.conv; +import tanya.conv; +import tanya.exception; public import tanya.memory.allocator; import tanya.memory.mmappool; import tanya.meta.trait; @@ -229,14 +229,14 @@ package(tanya) T[] resize(T)(shared Allocator allocator, } else { - onOutOfMemoryErrorNoGC(); + onOutOfMemoryError(); } } void[] buf = array; if (!allocator.reallocate(buf, length * T.sizeof)) { - onOutOfMemoryErrorNoGC(); + onOutOfMemoryError(); } // Casting from void[] is unsafe, but we know we cast to the original type. array = cast(T[]) buf; @@ -453,9 +453,7 @@ body { () @trusted { allocator.deallocate(mem); }(); } - - auto ptr = (() @trusted => (cast(T*) mem[0 .. stateSize!T].ptr))(); - return emplace!T(ptr, args); + return emplace!T(mem[0 .. stateSize!T], args); } /// diff --git a/source/tanya/memory/smartref.d b/source/tanya/memory/smartref.d index f142513..bc7999f 100644 --- a/source/tanya/memory/smartref.d +++ b/source/tanya/memory/smartref.d @@ -6,7 +6,13 @@ * Smart pointers. * * A smart pointer is an object that wraps a raw pointer or a reference - * (class, array) to manage its lifetime. + * (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-2017. * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, @@ -17,17 +23,17 @@ */ module tanya.memory.smartref; -import core.exception; import std.algorithm.comparison; import std.algorithm.mutation; -import std.conv; +import tanya.conv; +import tanya.exception; import tanya.memory; import tanya.meta.trait; import tanya.range.primitive; private template Payload(T) { - static if (is(T == class) || is(T == interface) || isArray!T) + static if (isPolymorphicType!T || isArray!T) { alias Payload = T; } @@ -202,13 +208,6 @@ struct RefCounted(T) return this; } - private @nogc unittest - { - auto rc = defaultAllocator.refCounted!int(5); - rc = defaultAllocator.make!int(7); - assert(*rc == 7); - } - /// ditto ref typeof(this) opAssign(typeof(null)) { @@ -229,14 +228,6 @@ struct RefCounted(T) return this; } - private @nogc unittest - { - RefCounted!int rc; - assert(!rc.isInitialized); - rc = null; - assert(!rc.isInitialized); - } - /// ditto ref typeof(this) opAssign(typeof(this) rhs) { @@ -308,7 +299,7 @@ struct RefCounted(T) } /// -unittest +@nogc @system unittest { auto rc = RefCounted!int(defaultAllocator.make!int(5), defaultAllocator); auto val = rc.get(); @@ -324,7 +315,22 @@ unittest assert(*rc.storage.payload == 9); } -private @nogc unittest +@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); @@ -340,7 +346,7 @@ private @nogc unittest assert(*rc == 5); } -private @nogc unittest +@nogc @system unittest { RefCounted!int rc; @@ -355,7 +361,7 @@ private @nogc unittest assert(rc.count == 0); } -private unittest +@nogc @system unittest { RefCounted!int rc1, rc2; static assert(is(typeof(rc1 = rc2))); @@ -389,7 +395,7 @@ version (unittest) } } -private @nogc unittest +@nogc @system unittest { uint destroyed; auto a = defaultAllocator.make!A(destroyed); @@ -399,7 +405,7 @@ private @nogc unittest auto rc = RefCounted!A(a, defaultAllocator); assert(rc.count == 1); - void func(RefCounted!A rc) @nogc + void func(RefCounted!A rc) @nogc @system { assert(rc.count == 2); } @@ -415,14 +421,14 @@ private @nogc unittest assert(rc.count == 1); } -private @nogc unittest +@nogc @system unittest { auto rc = RefCounted!int(defaultAllocator); assert(!rc.isInitialized); assert(rc.allocator is defaultAllocator); } -private @nogc unittest +@nogc @system unittest { auto rc = defaultAllocator.refCounted!int(5); assert(rc.count == 1); @@ -444,7 +450,7 @@ private @nogc unittest assert(rc.count == 0); } -private unittest +@nogc @system unittest { auto rc = defaultAllocator.refCounted!int(5); assert(*rc == 5); @@ -460,7 +466,7 @@ private unittest assert(*rc == 5); } -private unittest +@nogc nothrow pure @safe unittest { static assert(is(typeof(RefCounted!int.storage.payload) == int*)); static assert(is(typeof(RefCounted!A.storage.payload) == A)); @@ -511,17 +517,9 @@ body { () @trusted { allocator.deallocate(mem); }(); } - rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]); + rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); + rc.storage.payload = emplace!T(mem[storageSize .. $], args); - static if (is(T == class)) - { - rc.storage.payload = emplace!T(mem[storageSize .. $], args); - } - else - { - auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))(); - rc.storage.payload = emplace!T(ptr, args); - } rc.deleter = &unifiedDeleter!(Payload!T); return rc; } @@ -554,7 +552,7 @@ body } /// -unittest +@nogc @system unittest { auto rc = defaultAllocator.refCounted!int(5); assert(rc.count == 1); @@ -575,7 +573,7 @@ unittest assert(rc.count == 1); } -private @nogc unittest +@nogc @system unittest { struct E { @@ -597,13 +595,13 @@ private @nogc unittest } } -private @nogc unittest +@nogc @system unittest { auto rc = defaultAllocator.refCounted!(int[])(5); assert(rc.length == 5); } -private @nogc unittest +@nogc @system unittest { auto p1 = defaultAllocator.make!int(5); auto p2 = p1; @@ -611,13 +609,13 @@ private @nogc unittest assert(rc.get() is p2); } -private @nogc unittest +@nogc @system unittest { static bool destroyed = false; - struct F + static struct F { - ~this() @nogc + ~this() @nogc nothrow @safe { destroyed = true; } @@ -723,7 +721,7 @@ struct Unique(T) } /// - @nogc unittest + @nogc nothrow pure @system unittest { auto rc = defaultAllocator.unique!int(5); rc = defaultAllocator.make!int(7); @@ -770,7 +768,7 @@ struct Unique(T) } /// - @nogc unittest + @nogc nothrow pure @system unittest { Unique!int u; assert(!u.isInitialized); @@ -789,7 +787,7 @@ struct Unique(T) } /// - @nogc unittest + @nogc nothrow pure @system unittest { auto u = defaultAllocator.unique!int(5); assert(u.isInitialized); @@ -804,7 +802,7 @@ struct Unique(T) } /// -@nogc unittest +@nogc nothrow pure @system unittest { auto p = defaultAllocator.make!int(5); auto s = Unique!int(p, defaultAllocator); @@ -812,13 +810,13 @@ struct Unique(T) } /// -@nogc unittest +@nogc nothrow @system unittest { static bool destroyed = false; - struct F + static struct F { - ~this() @nogc + ~this() @nogc nothrow @safe { destroyed = true; } @@ -885,13 +883,13 @@ body return Unique!T(payload, allocator); } -private unittest +@nogc nothrow pure @safe unittest { static assert(is(typeof(defaultAllocator.unique!B(5)))); static assert(is(typeof(defaultAllocator.unique!(int[])(5)))); } -private unittest +@nogc nothrow pure @system unittest { auto s = defaultAllocator.unique!int(5); assert(*s == 5); @@ -900,7 +898,7 @@ private unittest assert(s is null); } -private unittest +@nogc nothrow pure @system unittest { auto s = defaultAllocator.unique!int(5); assert(*s == 5); @@ -909,7 +907,7 @@ private unittest assert(*s == 4); } -private @nogc unittest +@nogc nothrow pure @system unittest { auto p1 = defaultAllocator.make!int(5); auto p2 = p1; @@ -918,7 +916,7 @@ private @nogc unittest assert(rc.get() is p2); } -private @nogc unittest +@nogc nothrow pure @system unittest { auto rc = Unique!int(defaultAllocator); assert(rc.allocator is defaultAllocator);