diff --git a/README.md b/README.md index fc8c053..df6181b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ Garbage Collector heap. Everything in the library is usable in @nogc code. Tanya extends Phobos functionality and provides alternative implementations for data structures and utilities that depend on the Garbage Collector in Phobos. -* [Bug tracker](https://issues.caraus.io/projects/tanya/issues) * [API Documentation](https://docs.caraus.io/tanya) * [Contribution guidelines](CONTRIBUTING.md) @@ -24,6 +23,7 @@ data structures and utilities that depend on the Garbage Collector in Phobos. Tanya consists of the following packages and (top-level) modules: +* `algorithm`: Collection of generic algorithms. * `async`: Event loop (epoll, kqueue and IOCP). * `container`: Queue, Array, Singly and doubly linked lists, Buffers, UTF-8 string, Hash set. diff --git a/source/tanya/algorithm/mutation.d b/source/tanya/algorithm/mutation.d new file mode 100644 index 0000000..698dfdf --- /dev/null +++ b/source/tanya/algorithm/mutation.d @@ -0,0 +1,164 @@ +/* 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/. */ + +/** + * Algorithms that modify its arguments. + * + * 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/algorithm/mutation.d, + * tanya/algorithm/mutation.d) + */ +module tanya.algorithm.mutation; + +import tanya.memory.op; +import tanya.meta.trait; + +/** + * 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"); +} +body +{ + static if (is(T == struct)) + { + copy((&source)[0 .. 1], (&target)[0 .. 1]); + + static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T) + { + static const T init = T.init; + + static if (isNested!T) + { + // Don't override the context pointer. + enum size_t size = T.sizeof - (void*).sizeof; + copy((cast(void*) &init)[0 .. size], (&source)[0 .. 1]); + } + else + { + copy((&init)[0 .. 1], (&source)[0 .. 1]); + } + } + } + else + { + target = source; + } +} + +/// +@nogc nothrow pure @system unittest +{ + static struct S + { + int member = 5; + + this(this) @nogc nothrow pure @safe + { + assert(false); + } + } + S source, target = void; + moveEmplace(source, target); + assert(target.member == 5); + + int x1 = 5, x2; + moveEmplace(x1, x2); + assert(x2 == 5); +} + +/** + * Moves $(D_PARAM source) into $(D_PARAM target) assuming that + * $(D_PARAM target) isn't initialized. + * + * Moving the $(D_PARAM source) copies it into the $(D_PARAM target) and places + * the $(D_PARAM source) into a valid but unspecified state, which means that + * after moving $(D_PARAM source) can be destroyed or assigned a new value, but + * accessing it yields an unspecified value. $(D_PARAM target) is destroyed before + * the new value is assigned. If $(D_PARAM target) isn't initialized and + * therefore shouldn't be destroyed, $(D_PSYMBOL moveEmplace) can be used. + * + * If $(D_PARAM target) isn't specified, $(D_PSYMBOL move) returns the source + * as rvalue without calling its copy constructor or destructor. + * + * $(D_PARAM source) and $(D_PARAM target) are the same object, + * $(D_PSYMBOL move) does nothing. + * + * Params: + * T = Object type. + * source = Source object. + * target = Target object. + * + * See_Also: $(D_PSYMBOL moveEmplace). + */ +void move(T)(ref T source, ref T target) +{ + if ((() @trusted => &source is &target)()) + { + return; + } + static if (hasElaborateDestructor!T) + { + target.__xdtor(); + } + (() @trusted => moveEmplace(source, target))(); +} + +/// ditto +T move(T)(ref T source) +{ + T target = void; + (() @trusted => moveEmplace(source, target))(); + return target; +} + +/// +@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); +} diff --git a/source/tanya/algorithm/package.d b/source/tanya/algorithm/package.d new file mode 100644 index 0000000..e147073 --- /dev/null +++ b/source/tanya/algorithm/package.d @@ -0,0 +1,17 @@ +/* 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/. */ + +/** + * Collection of generic algorithms. + * + * 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/algorithm/package.d, + * tanya/algorithm/package.d) + */ +module tanya.algorithm; + +public import tanya.algorithm.mutation; diff --git a/source/tanya/container/array.d b/source/tanya/container/array.d index 0af61c7..4891588 100644 --- a/source/tanya/container/array.d +++ b/source/tanya/container/array.d @@ -16,8 +16,16 @@ module tanya.container.array; import core.checkedint; import std.algorithm.comparison; -import std.algorithm.mutation; +import std.algorithm.mutation : bringToFront, + copy, + fill, + initializeAll, + moveEmplaceAll, + moveAll, + swap, + uninitializedFill; import std.meta; +import tanya.algorithm.mutation; import tanya.exception; import tanya.memory; import tanya.meta.trait; diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d index aa338d0..696d1e9 100644 --- a/source/tanya/container/list.d +++ b/source/tanya/container/list.d @@ -15,11 +15,13 @@ module tanya.container.list; import std.algorithm.comparison; -import std.algorithm.mutation; +import std.algorithm.mutation : swap; import std.algorithm.searching; +import tanya.algorithm.mutation; import tanya.container.entry; import tanya.memory; import tanya.meta.trait; +import tanya.meta.transform; import tanya.range.array; import tanya.range.primitive; diff --git a/source/tanya/container/queue.d b/source/tanya/container/queue.d index 33144c0..7a42e85 100644 --- a/source/tanya/container/queue.d +++ b/source/tanya/container/queue.d @@ -14,7 +14,7 @@ */ module tanya.container.queue; -import std.algorithm.mutation; +import tanya.algorithm.mutation; import tanya.container.entry; import tanya.exception; import tanya.memory; diff --git a/source/tanya/container/set.d b/source/tanya/container/set.d index ad77ee9..3bf0dc5 100644 --- a/source/tanya/container/set.d +++ b/source/tanya/container/set.d @@ -15,7 +15,8 @@ */ module tanya.container.set; -import std.algorithm.mutation; +import std.algorithm.mutation : swap; +import tanya.algorithm.mutation; import tanya.container; import tanya.container.entry; import tanya.memory; diff --git a/source/tanya/container/string.d b/source/tanya/container/string.d index 1873af1..ca5b946 100644 --- a/source/tanya/container/string.d +++ b/source/tanya/container/string.d @@ -27,9 +27,10 @@ module tanya.container.string; import std.algorithm.comparison; -import std.algorithm.mutation; +import std.algorithm.mutation : bringToFront, copy, swap; import std.algorithm.searching; static import std.range; +import tanya.algorithm.mutation; import tanya.memory; import tanya.meta.trait; import tanya.meta.transform; diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d index f7d4b76..6980a29 100644 --- a/source/tanya/math/mp.d +++ b/source/tanya/math/mp.d @@ -14,11 +14,13 @@ */ module tanya.math.mp; -import std.algorithm; +import std.algorithm.comparison; +import std.algorithm.mutation; import std.range; import tanya.container.array; import tanya.encoding.ascii; import tanya.memory; +static import tanya.memory.op; import tanya.meta.trait; import tanya.meta.transform; @@ -207,7 +209,7 @@ struct Integer this(this) @nogc nothrow pure @safe { auto tmp = allocator.resize!digit(null, this.size); - this.rep[0 .. this.size].copy(tmp); + tanya.memory.op.copy(this.rep[0 .. this.size], tmp); this.rep = tmp; } @@ -340,7 +342,8 @@ struct Integer if (is(Unqual!T == Integer)) { this.rep = allocator.resize(this.rep, value.size); - value.rep[0 .. value.size].copy(this.rep[0 .. value.size]); + tanya.memory.op.copy(value.rep[0 .. value.size], + this.rep[0 .. value.size]); this.size = value.size; this.sign = value.sign; @@ -1023,7 +1026,10 @@ struct Integer Integer opUnary(string op : "~")() const { auto ret = Integer(this, allocator); - ret.rep[0 .. ret.size].each!((ref a) => a = ~a & mask); + foreach (ref a; ret.rep[0 .. ret.size]) + { + a = ~a & mask; + } return ret; }