summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2017-10-10 06:59:34 +0200
committerEugen Wissner <belka@caraus.de>2017-10-10 06:59:34 +0200
commit5d6f8e52999647fd82b93669a67a2bc1545980f5 (patch)
tree70b68f38dccd294d8740182e74a526cbc01c0822
parent87bfd77373d6d4f16528e7d7c14a33e751f88fab (diff)
downloadtanya-5d6f8e52999647fd82b93669a67a2bc1545980f5.tar.gz
Implement pure onOutOfMemory
-rw-r--r--README.md3
-rw-r--r--source/tanya/container/array.d14
-rw-r--r--source/tanya/container/queue.d2
-rw-r--r--source/tanya/container/string.d140
-rw-r--r--source/tanya/conv.d235
-rw-r--r--source/tanya/exception.d66
-rw-r--r--source/tanya/memory/package.d12
-rw-r--r--source/tanya/memory/smartref.d114
8 files changed, 451 insertions, 135 deletions
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);