From a20649458df95584ffe73900dc76ff26665840b0 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Sun, 11 Nov 2018 21:46:11 +0000 Subject: [PATCH 01/22] [WIP] Add @nogc array --- mak/COPY | 2 + mak/DOCS | 2 + mak/SRCS | 2 + mak/WINDOWS | 3 + src/core/experimental/array.d | 2559 +++++++++++++++++++++++++++++++++ 5 files changed, 2568 insertions(+) create mode 100644 src/core/experimental/array.d diff --git a/mak/COPY b/mak/COPY index 9e0664a4c6..ac3ecac984 100644 --- a/mak/COPY +++ b/mak/COPY @@ -21,6 +21,8 @@ COPY=\ $(IMPDIR)\core\time.d \ $(IMPDIR)\core\vararg.d \ \ + $(IMPDIR)\core\experimental\array.d \ + \ $(IMPDIR)\core\internal\abort.d \ $(IMPDIR)\core\internal\arrayop.d \ $(IMPDIR)\core\internal\convert.d \ diff --git a/mak/DOCS b/mak/DOCS index 96121956cd..7727240a56 100644 --- a/mak/DOCS +++ b/mak/DOCS @@ -20,6 +20,8 @@ DOCS=\ $(DOCDIR)\core_gc_gcinterface.html \ $(DOCDIR)\core_gc_registry.html \ \ + $(DOCDIR)\core_experimental_array.html \ + \ $(DOCDIR)\core_stdc_assert_.html \ $(DOCDIR)\core_stdc_config.html \ $(DOCDIR)\core_stdc_complex.html \ diff --git a/mak/SRCS b/mak/SRCS index 0904b9029e..4e587d7da5 100644 --- a/mak/SRCS +++ b/mak/SRCS @@ -21,6 +21,8 @@ SRCS=\ src\core\gc\gcinterface.d \ src\core\gc\registry.d \ \ + src\core\experimental\array.d \ + \ src\core\internal\abort.d \ src\core\internal\arrayop.d \ src\core\internal\convert.d \ diff --git a/mak/WINDOWS b/mak/WINDOWS index d75f9c19ad..88acd15dc7 100644 --- a/mak/WINDOWS +++ b/mak/WINDOWS @@ -113,6 +113,9 @@ $(IMPDIR)\core\gc\gcinterface.d : src\core\gc\gcinterface.d $(IMPDIR)\core\gc\registry.d : src\core\gc\registry.d copy $** $@ +$(IMPDIR)\core\experimental\array.d : src\core\experimental\array.d + copy $** $@ + $(IMPDIR)\core\internal\abort.d : src\core\internal\abort.d copy $** $@ diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d new file mode 100644 index 0000000000..7dc613365b --- /dev/null +++ b/src/core/experimental/array.d @@ -0,0 +1,2559 @@ +/// +module core.experimental.array; + +// { "Imports" from Phobos + +// Range functions + +/** +Implements the range interface primitive `front` for built-in arrays. Due to the +fact that nonmember functions can be called with the first argument using the dot +notation, `array.front` is equivalent to `front(array)`. For $(GLOSSARY narrow strings), +`front` automatically returns the first $(GLOSSARY code point) as _a `dchar`. +*/ +@property ref T front(T)(T[] a) @safe pure nothrow @nogc +//TODO: if (!isNarrowString!(T[]) && !is(T[] == void[])) +if (!is(T[] == void[])) +{ + assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); + return a[0]; +} + +/** +Implements the range interface primitive `empty` for types that obey `hasLength` +property and for narrow strings. + */ +@property bool empty(T)(auto ref scope const(T) a) +//TODO: if (is(typeof(a.length) : size_t) || isNarrowString!T) +if (is(typeof(a.length) : size_t)) +{ + return !a.length; +} + +/** +Implements the range interface primitive `popFront` for built-in arrays. For +$(GLOSSARY narrow strings), `popFront` automatically advances to the next +$(GLOSSARY code point). +*/ +void popFront(T)(ref T[] a) @safe pure nothrow @nogc +//TODO: if (!isNarrowString!(T[]) && !is(T[] == void[])) +if (!is(T[] == void[])) +{ + assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof); + a = a[1 .. $]; +} + +/** +This is a best-effort implementation of `length` for any kind of range. +*/ +auto walkLength(Range)(Range range) +if (isInputRange!Range && !isInfinite!Range) +{ + static if (hasLength!Range) + return range.length; + else + { + size_t result; + for ( ; !range.empty ; range.popFront() ) + ++result; + return result; + } +} + +/** +*/ +enum bool isInputRange(R) = + is(typeof(R.init) == R) + && is(typeof(R.init.empty()) == bool) + && is(typeof((return ref R r) => r.front)) + && !is(typeof(R.init.front()) == void) + && is(typeof((R r) => r.popFront)); + +/** +*/ +template isInfinite(R) +{ + static if (isInputRange!R && __traits(compiles, { enum e = R.empty; })) + enum bool isInfinite = !R.empty; + else + enum bool isInfinite = false; +} + +// End Range functions + +/** +Yields `true` if and only if `T` is an aggregate that defines a symbol called `name`. + */ +enum hasMember(T, string name) = __traits(hasMember, T, name); + +/** +Detect whether type `T` is an array (static or dynamic. +*/ +enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; + +/** + * Detect whether type `T` is a static array. + */ +enum bool isStaticArray(T) = __traits(isStaticArray, T); + +/** + * Detect whether type `T` is a dynamic array. + */ +enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T; + +/* + */ +template DynamicArrayTypeOf(T) +{ + static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) + alias X = DynamicArrayTypeOf!AT; + else + alias X = OriginalType!T; + + static if (is(Unqual!X : E[], E) && !is(typeof({ enum n = X.length; }))) + { + alias DynamicArrayTypeOf = X; + } + else + static assert(0, T.stringof~" is not a dynamic array"); +} + +// SomethingTypeOf +private template AliasThisTypeOf(T) +if (isAggregateType!T) +{ + alias members = AliasSeq!(__traits(getAliasThis, T)); + + static if (members.length == 1) + { + alias AliasThisTypeOf = typeof(__traits(getMember, T.init, members[0])); + } + else + static assert(0, T.stringof~" does not have alias this type"); +} + +template AliasSeq(TList...) +{ + alias AliasSeq = TList; +} + +/** + * Strips off all `enum`s from type `T`. + */ +template OriginalType(T) +{ + template Impl(T) + { + static if (is(T U == enum)) alias Impl = OriginalType!U; + else alias Impl = T; + } + + alias OriginalType = ModifyTypePreservingTQ!(Impl, T); +} + +// [For internal use] +private template ModifyTypePreservingTQ(alias Modifier, T) +{ + static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U; + else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U; + else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U; + else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U; + else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U; + else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U; + else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U; + else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U; + else alias ModifyTypePreservingTQ = Modifier!T; +} + +/** + * Detect whether type `T` is an aggregate type. + */ +enum bool isAggregateType(T) = is(T == struct) || is(T == union) || + is(T == class) || is(T == interface); + +/** +Removes all qualifiers, if any, from type `T`. + */ +template Unqual(T) +{ + version (none) // Error: recursive alias declaration @@@BUG1308@@@ + { + static if (is(T U == const U)) alias Unqual = Unqual!U; + else static if (is(T U == immutable U)) alias Unqual = Unqual!U; + else static if (is(T U == inout U)) alias Unqual = Unqual!U; + else static if (is(T U == shared U)) alias Unqual = Unqual!U; + else alias Unqual = T; + } + else // workaround + { + static if (is(T U == immutable U)) alias Unqual = U; + else static if (is(T U == shared inout const U)) alias Unqual = U; + else static if (is(T U == shared inout U)) alias Unqual = U; + else static if (is(T U == shared const U)) alias Unqual = U; + else static if (is(T U == shared U)) alias Unqual = U; + else static if (is(T U == inout const U)) alias Unqual = U; + else static if (is(T U == inout U)) alias Unqual = U; + else static if (is(T U == const U)) alias Unqual = U; + else alias Unqual = T; + } +} + +/** +Is `From` implicitly convertible to `To`? + */ +enum bool isImplicitlyConvertible(From, To) = is(From : To); + +/** +The element type of `R`. `R` does not have to be a range. The element type is +determined as the type yielded by `r.front` for an object `r` of type `R`. For +example, `ElementType!(T[])` is `T` if `T[]` isn't a narrow string; if it is, the +element type is `dchar`. If `R` doesn't have `front`, `ElementType!R` is `void`. + */ +template ElementType(R) +{ + static if (is(typeof(R.init.front.init) T)) + alias ElementType = T; + else + alias ElementType = void; +} + +/** +Yields `true` if `R` has a `length` member that returns a value of `size_t` +type. `R` does not have to be a range. If `R` is a range, algorithms in the +standard library are only guaranteed to support `length` with type `size_t`. +*/ +template hasLength(R) +{ + static if (is(typeof(((R* r) => r.length)(null)) Length)) + enum bool hasLength = is(Length == size_t); + //TODO: enum bool hasLength = is(Length == size_t) && !isNarrowString!R; + else + enum bool hasLength = false; +} + +// { Allocators + +/** +Returns the size in bytes of the state that needs to be allocated to hold an +object of type `T`. `stateSize!T` is zero for `struct`s that are not +nested and have no nonstatic member variables. + */ +template stateSize(T) +{ + static if (is(T == class) || is(T == interface)) + enum stateSize = __traits(classInstanceSize, T); + else static if (is(T == void)) + enum size_t stateSize = 0; + else + enum stateSize = T.sizeof; +} + +template isAbstractClass(T...) +if (T.length == 1) +{ + enum bool isAbstractClass = __traits(isAbstractClass, T[0]); +} + +template isInnerClass(T) +if (is(T == class)) +{ + static if (is(typeof(T.outer))) + enum isInnerClass = __traits(isSame, typeof(T.outer), __traits(parent, T)); + else + enum isInnerClass = false; +} + +enum classInstanceAlignment(T) = size_t.alignof >= T.alignof ? size_t.alignof : T.alignof; + +T emplace(T, Args...)(T chunk, auto ref Args args) +if (is(T == class)) +{ + static assert(!isAbstractClass!T, T.stringof ~ + " is abstract and it can't be emplaced"); + + // Initialize the object in its pre-ctor state + enum classSize = __traits(classInstanceSize, T); + (() @trusted => (cast(void*) chunk)[0 .. classSize] = typeid(T).initializer[])(); + + static if (isInnerClass!T) + { + static assert(Args.length > 0, + "Initializing an inner class requires a pointer to the outer class"); + static assert(is(Args[0] : typeof(T.outer)), + "The first argument must be a pointer to the outer class"); + + chunk.outer = args[0]; + alias args1 = args[1..$]; + } + else alias args1 = args; + + // Call the ctor if any + static if (is(typeof(chunk.__ctor(args1)))) + { + // T defines a genuine constructor accepting args + // Go the classic route: write .init first, then call ctor + chunk.__ctor(args1); + } + else + { + static assert(args1.length == 0 && !is(typeof(&T.__ctor)), + "Don't know how to initialize an object of type " + ~ T.stringof ~ " with arguments " ~ typeof(args1).stringof); + } + return chunk; +} + +T emplace(T, Args...)(void[] chunk, auto ref Args args) +if (is(T == class)) +{ + enum classSize = __traits(classInstanceSize, T); + testEmplaceChunk(chunk, classSize, classInstanceAlignment!T); + return emplace!T(cast(T)(chunk.ptr), args); +} + +T* emplace(T, Args...)(void[] chunk, auto ref Args args) +if (!is(T == class)) +{ + testEmplaceChunk(chunk, T.sizeof, T.alignof); + emplaceRef!(T, Unqual!T)(*cast(Unqual!T*) chunk.ptr, args); + return cast(T*) chunk.ptr; +} + +T* emplace(T)(T* chunk) @safe pure nothrow +{ + emplaceRef!T(*chunk); + return chunk; +} + +T* emplace(T, Args...)(T* chunk, auto ref Args args) +if (is(T == struct) || Args.length == 1) +{ + emplaceRef!T(*chunk, args); + return chunk; +} + +package void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) +{ + static if (args.length == 0) + { + static assert(is(typeof({static T i;})), + convFormat("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); + static if (is(T == class)) static assert(!isAbstractClass!T, + T.stringof ~ " is abstract and it can't be emplaced"); + emplaceInitializer(chunk); + } + else static if ( + !is(T == struct) && Args.length == 1 /* primitives, enums, arrays */ + || + Args.length == 1 && is(typeof({T t = args[0];})) /* conversions */ + || + is(typeof(T(args))) /* general constructors */) + { + static struct S + { + T payload; + this(ref Args x) + { + static if (Args.length == 1) + static if (is(typeof(payload = x[0]))) + payload = x[0]; + else + payload = T(x[0]); + else + payload = T(x); + } + } + if (__ctfe) + { + static if (is(typeof(chunk = T(args)))) + chunk = T(args); + else static if (args.length == 1 && is(typeof(chunk = args[0]))) + chunk = args[0]; + else assert(0, "CTFE emplace doesn't support " + ~ T.stringof ~ " from " ~ Args.stringof); + } + else + { + S* p = () @trusted { return cast(S*) &chunk; }(); + static if (UT.sizeof > 0) + emplaceInitializer(*p); + p.__ctor(args); + } + } + else static if (is(typeof(chunk.__ctor(args)))) + { + // This catches the rare case of local types that keep a frame pointer + emplaceInitializer(chunk); + chunk.__ctor(args); + } + else + { + //We can't emplace. Try to diagnose a disabled postblit. + static assert(!(Args.length == 1 && is(Args[0] : T)), + convFormat("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); + + //We can't emplace. + static assert(false, + convFormat("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); + } +} +// ditto +package void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) +if (is(UT == Unqual!UT)) +{ + emplaceRef!(UT, UT)(chunk, args); +} + +//emplace helper functions +private void emplaceInitializer(T)(scope ref T chunk) @trusted pure nothrow +{ + static if (__traits(isZeroInit, T)) + { + import core.stdc.string : memset; + memset(&chunk, 0, T.sizeof); + } + else + { + import core.stdc.string : memcpy; + static immutable T init = T.init; + memcpy(&chunk, &init, T.sizeof); + } +} + +private @nogc pure nothrow @safe +void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment) +{ + assert(chunk.length >= typeSize, "emplace: Chunk size too small."); + assert((cast(size_t) chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned."); +} + +enum hasElaborateDestructor(T) = __traits(compiles, { T t; t.__dtor(); }) + || __traits(compiles, { T t; t.__xdtor(); }); + +void dispose(A, T)(auto ref A alloc, auto ref T* p) +{ + static if (hasElaborateDestructor!T) + { + destroy(*p); + } + alloc.deallocate((cast(void*) p)[0 .. T.sizeof]); + static if (__traits(isRef, p)) + p = null; +} + +void dispose(A, T)(auto ref A alloc, auto ref T p) +if (is(T == class) || is(T == interface)) +{ + if (!p) return; + 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 support = (cast(void*) ob)[0 .. typeid(ob).initializer.length]; + destroy(p); + alloc.deallocate(support); + static if (__traits(isRef, p)) + p = null; +} + +void dispose(A, T)(auto ref A alloc, auto ref T[] array) +{ + static if (hasElaborateDestructor!(typeof(array[0]))) + { + foreach (ref e; array) + { + destroy(e); + } + } + alloc.deallocate(array); + static if (__traits(isRef, array)) + array = null; +} + +// } Allocators + +// } End "Imports" from Phobos + +auto tail(Collection)(Collection collection) +if (isInputRange!Collection) +{ + collection.popFront(); + return collection; +} + +auto equal(T, U)(T a, U b) +if (is(ElementType!T : ElementType!U) + && (is(T : V[], V) || is(T : Array!V2, V2)) + && (is(U : V4[], V4) || is(U : Array!V3, V3))) +{ + if (a.length != b.length) return false; + + while (!a.empty) + { + if (a.front != b.front) return false; + a.popFront(); + b.popFront(); + } + return true; +} + +@safe unittest +{ + auto a = [1, 2, 3]; + auto b = Array!int(a); + + assert(equal(a, a)); + assert(equal(a, b)); + assert(equal(b, a)); + assert(equal(b, b)); + a ~= 1; + assert(!equal(a, b)); +} + +struct PrefixAllocator +{ + /** + The alignment is a static constant equal to `platformAlignment`, which + ensures proper alignment for any D data type. + */ + enum uint alignment = size_t.alignof; + static enum prefixSize = size_t.sizeof; + + version(unittest) + { + // During unittesting, we are keeping a count of the number of bytes allocated + size_t bytesUsed; + } + + @trusted @nogc nothrow pure + void[] allocate(size_t bytes) shared + { + import core.memory : pureMalloc; + if (!bytes) return null; + auto p = pureMalloc(bytes + prefixSize); + + if (p is null) return null; + assert(cast(size_t) p % alignment == 0); + // Init reference count to 0 + *(cast(size_t *) p) = 0; + + version(unittest) + { + static if (is(typeof(this) == shared)) + { + import core.atomic : atomicOp; + atomicOp!"+="(bytesUsed, bytes); + } + else + { + bytesUsed += bytes; + } + } + + return p[prefixSize .. prefixSize + bytes]; + } + + @system @nogc nothrow pure + bool deallocate(void[] b) shared + { + import core.memory : pureFree; + assert(b !is null); + + version(unittest) + { + static if (is(typeof(this) == shared)) + { + import core.atomic : atomicOp; + assert(atomicOp!">="(bytesUsed, b.length)); + atomicOp!"-="(bytesUsed, b.length); + } + else + { + assert(bytesUsed >= b.length); + bytesUsed -= b.length; + } + } + + pureFree(b.ptr - prefixSize); + return true; + } + + private template Payload2Affix(Payload, Affix) + { + static if (is(Payload[] : void[])) + alias Payload2Affix = Affix; + else static if (is(Payload[] : shared(void)[])) + alias Payload2Affix = shared Affix; + else static if (is(Payload[] : immutable(void)[])) + alias Payload2Affix = shared Affix; + else static if (is(Payload[] : const(shared(void))[])) + alias Payload2Affix = shared Affix; + else static if (is(Payload[] : const(void)[])) + alias Payload2Affix = const Affix; + else + static assert(0, "Internal error for type " ~ Payload.stringof); + } + + static auto ref prefix(T)(T[] b) + { + assert(b.ptr && (cast(size_t) b.ptr % alignment == 0)); + return (cast(Payload2Affix!(T, size_t)*) b.ptr)[-1]; + } + + /** + Returns the global instance of this allocator type. The C heap allocator is + thread-safe, therefore all of its methods and `it` itself are `shared`. + */ + static shared PrefixAllocator instance; +} + +@safe pure nothrow @nogc unittest +{ + shared PrefixAllocator a; + auto b = a.allocate(42); + assert(b.length == 42); + assert(a.bytesUsed == 42); + () @trusted { + assert(a.prefix(b) == 0); + a.prefix(b)++; + assert(a.prefix(b) == 1); + a.deallocate(b); + }(); + assert(a.bytesUsed == 0); +} + + +version(unittest) +{ + private alias SCAlloc = shared PrefixAllocator; + private alias SSCAlloc = shared PrefixAllocator; + + SCAlloc _allocator; + SSCAlloc _sallocator; + + @nogc nothrow pure @trusted + void[] pureAllocate(bool isShared, size_t n) + { + return (cast(void[] function(bool, size_t) @nogc nothrow pure)(&_allocate))(isShared, n); + } + + @nogc nothrow @safe + void[] _allocate(bool isShared, size_t n) + { + return isShared ? _sallocator.allocate(n) : _allocator.allocate(n); + } + + static if (hasMember!(typeof(_allocator), "expand")) + { + @nogc nothrow pure @trusted + bool pureExpand(bool isShared, ref void[] b, size_t delta) + { + return (cast(bool function(bool, ref void[], size_t) @nogc nothrow pure)(&_expand))(isShared, b, delta); + } + + @nogc nothrow @safe + bool _expand(bool isShared, ref void[] b, size_t delta) + { + return isShared ? _sallocator.expand(b, delta) : _allocator.expand(b, delta); + } + } + + @nogc nothrow pure + void pureDispose(T)(bool isShared, T[] b) + { + return (cast(void function(bool, T[]) @nogc nothrow pure)(&_dispose!(T)))(isShared, b); + } + + @nogc nothrow + void _dispose(T)(bool isShared, T[] b) + { + return isShared ? _sallocator.dispose(b) : _allocator.dispose(b); + } +} + +/// +struct Array(T) +{ + import core.atomic : atomicOp; + + package T[] _payload; + package Unqual!T[] _support; + + version(unittest) + { + } + else + { + alias _allocator = shared PrefixAllocator.instance; + alias _sallocator = shared PrefixAllocator.instance; + } + +private: + + static enum double capacityFactor = 3.0 / 2; + static enum initCapacity = 3; + + bool _isShared; + + @trusted + auto pref() const + { + assert(_support !is null); + if (_isShared) + { + return _sallocator.prefix(_support); + } + else + { + return _allocator.prefix(_support); + } + } + + private size_t _opPrefix(string op, T)(const T[] support, size_t val) const + { + assert(support !is null); + if (_isShared) + { + return cast(size_t)(atomicOp!op(*cast(shared size_t *)&_sallocator.prefix(support), val)); + } + else + { + mixin("return cast(size_t)(*cast(size_t *)&_allocator.prefix(support)" ~ op ~ "val);"); + } + } + + @nogc nothrow pure @trusted + size_t opPrefix(string op, T)(const T[] support, size_t val) const + if ((op == "+=") || (op == "-=")) + { + + return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(support, val); + } + + @nogc nothrow pure @trusted + size_t opCmpPrefix(string op, T)(const T[] support, size_t val) const + if ((op == "==") || (op == "<=") || (op == "<") || (op == ">=") || (op == ">")) + { + return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(support, val); + } + + @nogc nothrow pure @trusted + void addRef(SupportQual, this Q)(SupportQual support) + { + assert(support !is null); + cast(void) opPrefix!("+=")(support, 1); + } + + void delRef(Unqual!T[] support) + { + // Will be optimized away, but the type system infers T's safety + if (0) { T t = T.init; } + + assert(support !is null); + if (opPrefix!("-=")(support, 1) == 0) + { + () @trusted { + version(unittest) + { + pureDispose(_isShared, support); + } + else + { + _allocator.dispose(support); + } + }(); + } + } + + static string immutableInsert(StuffType)(string stuff) + { + static if (hasLength!StuffType) + { + auto stuffLengthStr = q{ + size_t stuffLength = } ~ stuff ~ ".length;"; + } + else + { + auto stuffLengthStr = q{ + size_t stuffLength = walkLength(} ~ stuff ~ ");"; + } + + return stuffLengthStr ~ q{ + + version(unittest) + { + void[] tmpSupport = (() @trusted => pureAllocate(_isShared, stuffLength * stateSize!T))(); + } + else + { + void[] tmpSupport; + if (_isShared) + { + tmpSupport = (() @trusted => _sallocator.allocate(stuffLength * stateSize!T))(); + } + else + { + tmpSupport = (() @trusted => _allocator.allocate(stuffLength * stateSize!T))(); + } + } + + assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is null)); + size_t i = 0; + foreach (ref item; } ~ stuff ~ q{) + { + //writefln("the type is %s %s %s", T.stringof, typeof(_support).stringof, typeof(_payload).stringof); + alias TT = ElementType!(typeof(_payload)); + //pragma(msg, typeof(item).stringof, " TT is ", TT.stringof); + + size_t s = i * stateSize!TT; + size_t e = (i + 1) * stateSize!TT; + void[] tmp = tmpSupport[s .. e]; + i++; + (() @trusted => emplace!TT(tmp, item))(); + } + + _support = (() @trusted => cast(typeof(_support))(tmpSupport))(); + _payload = (() @trusted => cast(typeof(_payload))(_support[0 .. stuffLength]))(); + if (_support) addRef(_support); + }; + } + + void destroyUnused() + { + if (_support !is null) + { + delRef(_support); + } + } + +public: + /** + * Constructs a qualified array out of a number of items + * that will use the collection deciced allocator object. + * + * Params: + * values = a variable number of items, either in the form of a + * list or as a built-in array + * + * Complexity: $(BIGOH m), where `m` is the number of items. + */ + //version(none) + this(U, this Q)(U[] values...) + if (!is(Q == shared) + && isImplicitlyConvertible!(U, T)) + { + static if (is(Q == immutable) || is(Q == const)) + { + _isShared = true; + mixin(immutableInsert!(typeof(values))("values")); + } + else + { + insert(0, values); + } + } + + /// + static if (is(T == int)) + @safe unittest + { + // Create a list from a list of ints + { + auto a = Array!int(1, 2, 3); + assert(equal(a, [1, 2, 3])); + } + // Create a list from an array of ints + { + auto a = Array!int([1, 2, 3]); + assert(equal(a, [1, 2, 3])); + } + // Create a list from a list from an input range + { + auto a = Array!int(1, 2, 3); + auto a2 = Array!int(a); + assert(equal(a2, [1, 2, 3])); + } + } + + /** + * Constructs a qualified array out of an + * $(REF_ALTTEXT input range, isInputRange, std,range,primitives) + * that will use the collection decided allocator object. + * If `Stuff` defines `length`, `Array` will use it to reserve the + * necessary amount of memory. + * + * Params: + * stuff = an input range of elements that are implitictly convertible + * to `T` + * + * Complexity: $(BIGOH m), where `m` is the number of elements in the range. + */ + this(Stuff, this Q)(Stuff stuff) + if (!is(Q == shared) + && isInputRange!Stuff && !isInfinite!Stuff + && isImplicitlyConvertible!(ElementType!Stuff, T) + && !is(Stuff == T[])) + { + static if (is(Q == immutable) || is(Q == const)) + { + _isShared = true; + mixin(immutableInsert!(typeof(stuff))("stuff")); + } + else + { + insert(0, stuff); + } + } + + // Begin Copy Ctors + // { + + private static enum copyCtorIncRef = q{ + _payload = rhs._payload; + _support = rhs._support; + _isShared = rhs._isShared; + + if (_support !is null) + { + addRef(_support); + } + }; + + this(ref typeof(this) rhs) + { + mixin(copyCtorIncRef); + } + + // { Get a const obj + + this(ref typeof(this) rhs) const + { + mixin(copyCtorIncRef); + } + + this(const ref typeof(this) rhs) const + { + mixin(copyCtorIncRef); + } + + this(immutable ref typeof(this) rhs) const + { + mixin(copyCtorIncRef); + } + // } Get a const obj + + // { Get an immutable obj + + this(ref typeof(this) rhs) immutable + { + _isShared = true; + mixin(immutableInsert!(typeof(rhs))("rhs")); + } + + this(const ref typeof(this) rhs) immutable + { + _isShared = rhs._isShared; + mixin(immutableInsert!(typeof(rhs._payload))("rhs._payload")); + } + + this(immutable ref typeof(this) rhs) immutable + { + mixin(copyCtorIncRef); + } + + // } Get an immutable obj + + // } + // End Copy Ctors + + // Immutable ctors + private this(SuppQual, PaylQual, this Qualified)(SuppQual support, PaylQual payload, bool isShared) + if (is(typeof(_support) : typeof(support))) + { + _support = support; + _payload = payload; + _isShared = isShared; + if (_support !is null) + { + addRef(_support); + } + } + + ~this() + { + destroyUnused(); + } + + static if (is(T == int)) + @nogc nothrow pure @safe unittest + { + auto a = Array!int(1, 2, 3); + + // Infer safety + static assert(!__traits(compiles, () @safe { Array!Unsafe(Unsafe(1)); })); + static assert(!__traits(compiles, () @safe { auto a = const Array!Unsafe(Unsafe(1)); })); + static assert(!__traits(compiles, () @safe { auto a = immutable Array!Unsafe(Unsafe(1)); })); + + static assert(!__traits(compiles, () @safe { Array!UnsafeDtor(UnsafeDtor(1)); })); + static assert(!__traits(compiles, () @safe { auto s = const Array!UnsafeDtor(UnsafeDtor(1)); })); + static assert(!__traits(compiles, () @safe { auto s = immutable Array!UnsafeDtor(UnsafeDtor(1)); })); + + // Infer purity + static assert(!__traits(compiles, () pure { Array!Impure(Impure(1)); })); + static assert(!__traits(compiles, () pure { auto a = const Array!Impure(Impure(1)); })); + static assert(!__traits(compiles, () pure { auto a = immutable Array!Impure(Impure(1)); })); + + static assert(!__traits(compiles, () pure { Array!ImpureDtor(ImpureDtor(1)); })); + static assert(!__traits(compiles, () pure { auto s = const Array!ImpureDtor(ImpureDtor(1)); })); + static assert(!__traits(compiles, () pure { auto s = immutable Array!ImpureDtor(ImpureDtor(1)); })); + + // Infer throwability + static assert(!__traits(compiles, () nothrow { Array!Throws(Throws(1)); })); + static assert(!__traits(compiles, () nothrow { auto a = const Array!Throws(Throws(1)); })); + static assert(!__traits(compiles, () nothrow { auto a = immutable Array!Throws(Throws(1)); })); + + static assert(!__traits(compiles, () nothrow { Array!ThrowsDtor(ThrowsDtor(1)); })); + static assert(!__traits(compiles, () nothrow { auto s = const Array!ThrowsDtor(ThrowsDtor(1)); })); + static assert(!__traits(compiles, () nothrow { auto s = immutable Array!ThrowsDtor(ThrowsDtor(1)); })); + } + + private @nogc nothrow pure @trusted + size_t slackFront() const + { + return _payload.ptr - _support.ptr; + } + + private @nogc nothrow pure @trusted + size_t slackBack() const + { + return _support.ptr + _support.length - _payload.ptr - _payload.length; + } + + /** + * Return the number of elements in the array.. + * + * Returns: + * the length of the array. + * + * Complexity: $(BIGOH 1). + */ + @nogc nothrow pure @safe + size_t length() const + { + return _payload.length; + } + + /// ditto + alias opDollar = length; + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int(1, 2, 3); + assert(a.length == 3); + assert(a[$ - 1] == 3); + } + + /** + * Set the length of the array to `len`. `len` must be less than or equal + * to the `capacity` of the array. + * + * Params: + * len = a positive integer + * + * Complexity: $(BIGOH 1). + */ + @nogc nothrow pure @trusted + void forceLength(size_t len) + { + assert(len <= capacity); + _payload = cast(T[])(_support[slackFront .. len]); + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int(1, 2, 3); + a.forceLength(2); + assert(a.length == 2); + } + + /** + * Get the available capacity of the `array`; this is equal to `length` of + * the array plus the available pre-allocated, free, space. + * + * Returns: + * a positive integer denoting the capacity. + * + * Complexity: $(BIGOH 1). + */ + @nogc nothrow pure @safe + size_t capacity() const + { + return length + slackBack; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int(1, 2, 3); + a.reserve(10); + assert(a.capacity == 10); + } + + /** + * Reserve enough memory from the allocator to store `n` elements. + * If the current `capacity` exceeds `n` nothing will happen. + * If `n` exceeds the current `capacity`, an attempt to `expand` the + * current array is made. If `expand` is successful, all the expanded + * elements are default initialized to `T.init`. If the `expand` fails + * a new buffer will be allocated, the old elements of the array will be + * copied and the new elements will be default initialized to `T.init`. + * + * Params: + * n = a positive integer + * + * Complexity: $(BIGOH max(length, n)). + */ + void reserve(size_t n) + { + // Will be optimized away, but the type system infers T's safety + if (0) { T t = T.init; } + + if (n <= capacity) { return; } + + static if (hasMember!(typeof(_allocator), "expand")) + if (_support && opCmpPrefix!"=="(_support, 1)) + { + void[] buf = _support; + version(unittest) + { + auto successfulExpand = pureExpand(_isShared, buf, (n - capacity) * stateSize!T); + } + else + { + auto successfulExpand = _allocator.expand(buf, (n - capacity) * stateSize!T); + } + + if (successfulExpand) + { + const oldLength = _support.length; + _support = (() @trusted => cast(Unqual!T[])(buf))(); + // Emplace extended buf + // TODO: maybe? emplace only if T has indirections + foreach (i; oldLength .. _support.length) + { + emplace(&_support[i]); + } + return; + } + else + { + assert(0, "Array.reserve: Failed to expand array."); + } + } + + version(unittest) + { + auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(_isShared, n * stateSize!T)))(); + } + else + { + auto tmpSupport = (() @trusted => cast(Unqual!T[])(_allocator.allocate(n * stateSize!T)))(); + } + assert(tmpSupport !is null); + for (size_t i = 0; i < tmpSupport.length; ++i) + { + if (i < _payload.length) + { + emplace(&tmpSupport[i], _payload[i]); + //pragma(msg, typeof(&tmpSupport[i]).stringof); + } + else + { + emplace(&tmpSupport[i]); + } + } + + destroyUnused(); + _support = tmpSupport; + addRef(_support); + _payload = (() @trusted => cast(T[])(_support[0 .. _payload.length]))(); + assert(capacity >= n); + } + + /// + static if (is(T == int)) + @safe unittest + { + auto stuff = [1, 2, 3]; + Array!int a; + a.reserve(stuff.length); + a ~= stuff; + assert(equal(a, stuff)); + } + + /** + * Inserts the elements of an + * $(REF_ALTTEXT input range, isInputRange, std,range,primitives), or a + * variable number of items, at the given `pos`. + * + * If no allocator was provided when the array was created, the + * $(REF, GCAllocator, std,experimental,allocator,gc_allocator) will be used. + * If `Stuff` defines `length`, `Array` will use it to reserve the + * necessary amount of memory. + * + * Params: + * pos = a positive integer + * stuff = an input range of elements that are implitictly convertible + * to `T`; a variable number of items either in the form of a + * list or as a built-in array + * + * Returns: + * the number of elements inserted + * + * Complexity: $(BIGOH max(length, pos + m)), where `m` is the number of + * elements in the range. + */ + size_t insert(Stuff)(size_t pos, Stuff stuff) + if (!isArray!(typeof(stuff)) && isInputRange!Stuff && !isInfinite!Stuff + && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + // Will be optimized away, but the type system infers T's safety + if (0) { T t = T.init; } + + static if (hasLength!Stuff) + { + size_t stuffLength = stuff.length; + } + else + { + size_t stuffLength = walkLength(stuff); + } + if (stuffLength == 0) return 0; + + version(unittest) + { + auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(_isShared, stuffLength * stateSize!T)))(); + } + else + { + auto tmpSupport = (() @trusted => cast(Unqual!T[])(_allocator.allocate(stuffLength * stateSize!T)))(); + } + assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is null)); + for (size_t i = 0; i < tmpSupport.length; ++i) + { + emplace(&tmpSupport[i]); + } + + size_t i = 0; + foreach (ref item; stuff) + { + tmpSupport[i++] = item; + } + size_t result = insert(pos, tmpSupport); + version(unittest) + { + () @trusted { pureDispose(_isShared, tmpSupport); }(); + } + else + { + () @trusted { _allocator.dispose(tmpSupport); }(); + } + return result; + } + + /// ditto + size_t insert(Stuff)(size_t pos, Stuff[] stuff...) + if (isImplicitlyConvertible!(Stuff, T)) + { + // Will be optimized away, but the type system infers T's safety + if (0) { T t = T.init; } + + assert(pos <= _payload.length); + + if (stuff.length == 0) return 0; + if (stuff.length > slackBack) + { + double newCapacity = capacity ? capacity * capacityFactor : stuff.length; + while (newCapacity < capacity + stuff.length) + { + newCapacity = newCapacity * capacityFactor; + } + reserve((() @trusted => cast(size_t)(newCapacity))()); + } + //_support[pos + stuff.length .. _payload.length + stuff.length] = + //_support[pos .. _payload.length]; + for (size_t i = _payload.length + stuff.length - 1; i >= pos + + stuff.length; --i) + { + // Avoids underflow if payload is empty + _support[i] = _support[i - stuff.length]; + } + + //writefln("typeof support[i] %s", typeof(_support[0]).stringof); + + //_support[pos .. pos + stuff.length] = stuff[]; + for (size_t i = pos, j = 0; i < pos + stuff.length; ++i, ++j) { + _support[i] = stuff[j]; + } + + _payload = (() @trusted => cast(T[])(_support[0 .. _payload.length + stuff.length]))(); + return stuff.length; + } + + /// + static if (is(T == int)) + @safe unittest + { + Array!int a; + assert(a.empty); + + size_t pos = 0; + pos += a.insert(pos, 1); + pos += a.insert(pos, [2, 3]); + assert(equal(a, [1, 2, 3])); + } + + /** + * Check whether there are no more references to this array instance. + * + * Returns: + * `true` if this is the only reference to this array instance; + * `false` otherwise. + * + * Complexity: $(BIGOH 1). + */ + @nogc nothrow pure @safe + bool isUnique(this _)() + { + if (_support !is null) + { + return cast(bool) opCmpPrefix!"=="(_support, 1); + } + return true; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int(24, 42); + + assert(a.isUnique); + { + auto a2 = a; + assert(!a.isUnique); + a2.front = 0; + assert(a.front == 0); + } // a2 goes out of scope + assert(a.isUnique); + } + + /** + * Check if the array is empty. + * + * Returns: + * `true` if there are no elements in the array; `false` otherwise. + * + * Complexity: $(BIGOH 1). + */ + @nogc nothrow pure @safe + bool empty() const + { + return length == 0; + } + + /// + static if (is(T == int)) + @safe unittest + { + Array!int a; + assert(a.empty); + size_t pos = 0; + a.insert(pos, 1); + assert(!a.empty); + } + + /** + * Provide access to the first element in the array. The user must check + * that the array isn't `empty`, prior to calling this function. + * + * Returns: + * a reference to the first element. + * + * Complexity: $(BIGOH 1). + */ + ref auto front(this _)() + { + assert(!empty, "Array.front: Array is empty"); + return _payload[0]; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int(1, 2, 3); + assert(a.front == 1); + a.front = 0; + assert(a.front == 0); + } + + /** + * Advance to the next element in the array. The user must check + * that the array isn't `empty`, prior to calling this function. + * + * Complexity: $(BIGOH 1). + */ + @nogc nothrow pure @safe + void popFront() + { + assert(!empty, "Array.popFront: Array is empty"); + _payload = _payload[1 .. $]; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto stuff = [1, 2, 3]; + auto a = Array!int(stuff); + size_t i = 0; + while (!a.empty) + { + assert(a.front == stuff[i++]); + a.popFront; + } + assert(a.empty); + } + + /** + * Advance to the next element in the array. The user must check + * that the array isn't `empty`, prior to calling this function. + * + * This must be used in order to iterate through a `const` or `immutable` + * array For a mutable array this is equivalent to calling `popFront`. + * + * Returns: + * an array that starts with the next element in the original array. + * + * Complexity: $(BIGOH 1). + */ + Qualified tail(this Qualified)() + { + assert(!empty, "Array.tail: Array is empty"); + + static if (is(Qualified == immutable) || is(Qualified == const)) + { + return this[1 .. $]; + } + else + { + return .tail(this); + } + } + + /// + static if (is(T == int)) + @safe unittest + { + auto ia = immutable Array!int([1, 2, 3]); + assert(ia.tail.front == 2); + } + + /** + * Eagerly iterate over each element in the array and call `fun` over each + * element. This should be used to iterate through `const` and `immutable` + * arrays. + * + * Normally, the entire array is iterated. If partial iteration (early stopping) + * is desired, `fun` needs to return a value of type `int` (`-1` to stop, or + * anything else to continue the iteration. + * + * Params: + * fun = unary function to apply on each element of the array. + * + * Returns: + * `true` if it has iterated through all the elements in the array, or + * `false` otherwise. + * + * Complexity: $(BIGOH n). + */ + template each(alias fun) + { + //import std.functional : unaryFun; + + bool each(this Q)() + //if (is (typeof(unaryFun!fun(T.init)))) + { + //alias fn = unaryFun!fun; + alias fn = fun; + + // Iterate through the underlying payload + // The array is kept alive (rc > 0) from the caller scope + foreach (ref e; this._payload) + { + static if (!is(typeof(fn(T.init)) == int)) + { + cast(void) fn(e); + } + else + { + if (fn(e) == -1) + return false; + } + } + return true; + } + } + + /// + static if (is(T == int)) + @safe unittest + { + auto ia = immutable Array!int([3, 2, 1]); + + static bool foo(int x) { return x > 0; } + static int bar(int x) { return x > 1 ? 1 : -1; } + + assert(ia.each!foo == true); + assert(ia.each!bar == false); + } + + @safe unittest + { + { + auto ia = immutable Array!int([3, 2, 1]); + + static bool foo(int x) { return x > 0; } + static int bar(int x) { return x > 1 ? 1 : -1; } + + assert(ia.each!foo == true); + assert(ia.each!bar == false); + } + + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + } + + //int opApply(int delegate(const ref T) dg) const + //{ + //if (_payload.length && dg(_payload[0])) return 1; + //if (!this.empty) this.tail.opApply(dg); + //return 0; + //} + + /** + * Perform a shallow copy of the array. + * + * Returns: + * a new reference to the current array. + * + * Complexity: $(BIGOH 1). + */ + ref auto save(this _)() + { + return this; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto stuff = [1, 2, 3]; + auto a = Array!int(stuff); + size_t i = 0; + + auto tmp = a.save; + while (!tmp.empty) + { + assert(tmp.front == stuff[i++]); + tmp.popFront; + } + assert(tmp.empty); + assert(!a.empty); + } + + /** + * Perform an immutable copy of the array. This will create a new array that + * will copy the elements of the current array. This will `NOT` call `dup` on + * the elements of the array, regardless if `T` defines it or not. If the array + * is already immutable, this will just create a new reference to it. + * + * Returns: + * an immutable array. + * + * Complexity: $(BIGOH n). + */ + immutable(Array!T) idup(this Q)() + { + auto r = immutable Array!T(this); + return r; + } + + /// + static if (is(T == int)) + @safe unittest + { + { + auto a = Array!(int)(1, 2, 3); + auto a2 = a.idup(); + static assert (is(typeof(a2) == immutable)); + } + + { + auto a = const Array!(int)(1, 2, 3); + auto a2 = a.idup(); + static assert (is(typeof(a2) == immutable)); + } + + { + auto a = immutable Array!(int)(1, 2, 3); + auto a2 = a.idup(); + static assert (is(typeof(a2) == immutable)); + } + } + + /** + * Perform a copy of the array. This will create a new array that will copy + * the elements of the current array. This will `NOT` call `dup` on the + * elements of the array, regardless if `T` defines it or not. + * + * Returns: + * a new mutable array. + * + * Complexity: $(BIGOH n). + */ + Array!T dup(this Q)() + { + Array!T result; + + static if (is(Q == immutable) || is(Q == const)) + { + result.reserve(length); + foreach(i; 0 .. length) + { + result ~= this[i]; + } + } + else + { + result.insert(0, this); + } + return result; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto stuff = [1, 2, 3]; + auto a = immutable Array!int(stuff); + auto aDup = a.dup; + assert(equal(aDup, stuff)); + aDup.front = 0; + assert(aDup.front == 0); + assert(a.front == 1); + } + + /** + * Return a slice to the current array. This is equivalent to calling + * `save`. + * + * Returns: + * an array that references the current array. + * + * Complexity: $(BIGOH 1) + */ + Qualified opSlice(this Qualified)() + { + return this.save; + } + + /** + * Return a slice to the current array that is bounded by `start` and `end`. + * `start` must be less than or equal to `end` and `end` must be less than + * or equal to `length`. + * + * Returns: + * an array that references the current array. + * + * Params: + * start = a positive integer + * end = a positive integer + * + * Complexity: $(BIGOH 1) + */ + Qualified opSlice(this Qualified)(size_t start, size_t end) + in + { + assert(start <= end && end <= length, + "Array.opSlice(s, e): Invalid bounds: Ensure start <= end <= length"); + } + body + { + return typeof(this)(_support, _payload[start .. end], _isShared); + } + + /// + static if (is(T == int)) + @safe unittest + { + auto stuff = [1, 2, 3]; + auto a = Array!int(stuff); + assert(equal(a[], stuff)); + assert(equal(a[1 .. $], stuff[1 .. $])); + } + + /** + * Provide access to the element at `idx` in the array. + * `idx` must be less than `length`. + * + * Returns: + * a reference to the element found at `idx`. + * + * Params: + * idx = a positive integer + * + * Complexity: $(BIGOH 1). + */ + ref auto opIndex(this _)(size_t idx) + in + { + assert(idx < length, "Array.opIndex: Index out of bounds"); + } + body + { + return _payload[idx]; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int([1, 2, 3]); + assert(a[2] == 3); + } + + /** + * Apply an unary operation to the element at `idx` in the array. + * `idx` must be less than `length`. + * + * Returns: + * a reference to the element found at `idx`. + * + * Params: + * idx = a positive integer + * + * Complexity: $(BIGOH 1). + */ + ref auto opIndexUnary(string op)(size_t idx) + in + { + assert(idx < length, "Array.opIndexUnary!" ~ op ~ ": Index out of bounds"); + } + body + { + mixin("return " ~ op ~ "_payload[idx];"); + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int([1, 2, 3]); + int x = --a[2]; + assert(a[2] == 2); + assert(x == 2); + } + + /** + * Assign `elem` to the element at `idx` in the array. + * `idx` must be less than `length`. + * + * Returns: + * a reference to the element found at `idx`. + * + * Params: + * elem = an element that is implicitly convertible to `T` + * idx = a positive integer + * + * Complexity: $(BIGOH 1). + */ + ref auto opIndexAssign(U)(U elem, size_t idx) + if (isImplicitlyConvertible!(U, T)) + in + { + assert(idx < length, "Array.opIndexAssign: Index out of bounds"); + } + body + { + return _payload[idx] = elem; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int([1, 2, 3]); + a[2] = 2; + assert(a[2] == 2); + (a[2] = 3)++; + assert(a[2] == 4); + } + + /** + Assign `elem` to all element in the array. + + Returns: + a reference to itself + + Params: + elem = an element that is implicitly convertible to `T` + + Complexity: $(BIGOH n). + */ + ref auto opIndexAssign(U)(U elem) + if (isImplicitlyConvertible!(U, T)) + body + { + _payload[] = elem; + return this; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int([1, 2, 3]); + a[] = 0; + assert(a.equal([0, 0, 0])); + } + + /** + Assign `elem` to the element at `idx` in the array. + `idx` must be less than `length`. + + Returns: + a reference to the element found at `idx`. + + Params: + elem = an element that is implicitly convertible to `T` + indices = a positive integer + + Complexity: $(BIGOH n). + */ + auto opSliceAssign(U)(U elem, size_t start, size_t end) + if (isImplicitlyConvertible!(U, T)) + in + { + assert(start <= end, "Array.opSliceAssign: Index out of bounds"); + assert(end < length, "Array.opSliceAssign: Index out of bounds"); + } + body + { + return _payload[start .. end] = elem; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int([1, 2, 3, 4, 5, 6]); + a[1 .. 3] = 0; + assert(a.equal([1, 0, 0, 4, 5, 6])); + } + + /** + * Assign to the element at `idx` in the array the result of + * $(D a[idx] op elem). + * `idx` must be less than `length`. + * + * Returns: + * a reference to the element found at `idx`. + * + * Params: + * elem = an element that is implicitly convertible to `T` + * idx = a positive integer + * + * Complexity: $(BIGOH 1). + */ + ref auto opIndexOpAssign(string op, U)(U elem, size_t idx) + if (isImplicitlyConvertible!(U, T)) + in + { + assert(idx < length, "Array.opIndexOpAssign!" ~ op ~ ": Index out of bounds"); + } + body + { + mixin("return _payload[idx]" ~ op ~ "= elem;"); + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int([1, 2, 3]); + a[2] += 2; + assert(a[2] == 5); + (a[2] += 3)++; + assert(a[2] == 9); + } + + /** + * Create a new array that results from the concatenation of this array + * with `rhs`. + * + * Params: + * rhs = can be an element that is implicitly convertible to `T`, an + * input range of such elements, or another `Array` + * + * Returns: + * the newly created array + * + * Complexity: $(BIGOH n + m), where `m` is the number of elements in `rhs`. + */ + auto ref opBinary(string op, U)(auto ref U rhs) + if (op == "~" && + (is (U : const typeof(this)) + || is (U : T) + || (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T)) + )) + { + auto newArray = this.dup(); + static if (is(U : const typeof(this))) + { + foreach(i; 0 .. rhs.length) + { + newArray ~= rhs[i]; + } + } + else + { + newArray.insert(length, rhs); + // Or + // newArray ~= rhs; + } + return newArray; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int(1); + auto a2 = a ~ 2; + + assert(equal(a2, [1, 2])); + a.front = 0; + assert(equal(a2, [1, 2])); + } + + /** + * Assign `rhs` to this array. The current array will now become another + * reference to `rhs`, unless `rhs` is `null`, in which case the current + * array will become empty. If `rhs` refers to the current array nothing will + * happen. + * + * If there are no more references to the previous array, the previous + * array will be destroyed; this leads to a $(BIGOH n) complexity. + * + * Params: + * rhs = a reference to an array + * + * Returns: + * a reference to this array + * + * Complexity: $(BIGOH n). + */ + auto ref opAssign()(auto ref typeof(this) rhs) + { + if (rhs._support !is null && _support is rhs._support) + { + if (rhs._payload is _payload) + return this; + } + + if (rhs._support !is null) + { + rhs.addRef(rhs._support); + } + destroyUnused(); + _support = rhs._support; + _payload = rhs._payload; + return this; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto a = Array!int(1); + auto a2 = Array!int(1, 2); + + a = a2; // this will free the old a + assert(equal(a, [1, 2])); + a.front = 0; + assert(equal(a2, [0, 2])); + } + + static if (is(T == int)) + @safe unittest + { + auto arr = Array!int(1, 2, 3, 4, 5, 6); + auto arr1 = arr[1 .. $]; + auto arr2 = arr[3 .. $]; + arr1 = arr2; + assert(arr1.equal([4, 5, 6])); + assert(arr2.equal([4, 5, 6])); + } + + /** + * Append the elements of `rhs` at the end of the array. + * + * If no allocator was provided when the list was created, the + * $(REF, GCAllocator, std,experimental,allocator,gc_allocator) will be used. + * + * Params: + * rhs = can be an element that is implicitly convertible to `T`, an + * input range of such elements, or another `Array` + * + * Returns: + * a reference to this array + * + * Complexity: $(BIGOH n + m), where `m` is the number of elements in `rhs`. + */ + auto ref opOpAssign(string op, U)(auto ref U rhs) + if (op == "~" && + (is (U == typeof(this)) + || is (U : T) + || (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T)) + )) + { + insert(length, rhs); + return this; + } + + /// + static if (is(T == int)) + @safe unittest + { + Array!int a; + auto a2 = Array!int(4, 5); + assert(a.empty); + + a ~= 1; + a ~= [2, 3]; + assert(equal(a, [1, 2, 3])); + + // append an input range + a ~= a2; + assert(equal(a, [1, 2, 3, 4, 5])); + a2.front = 0; + assert(equal(a, [1, 2, 3, 4, 5])); + } + + /// + bool opEquals()(auto ref typeof(this) rhs) const + { + return _support.equal(rhs); + } + + /// + static if (is(T == int)) + @safe unittest + { + auto arr1 = Array!int(1, 2); + auto arr2 = Array!int(1, 2); + auto arr3 = Array!int(2, 3); + assert(arr1 == arr2); + assert(arr2 == arr1); + assert(arr1 != arr3); + assert(arr3 != arr1); + assert(arr2 != arr3); + assert(arr3 != arr2); + } + + /// + int opCmp(U)(auto ref U rhs) + if (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T)) + { + auto r1 = this; + auto r2 = rhs; + for (; !r1.empty && !r2.empty; r1.popFront, r2.popFront) + { + if (r1.front < r2.front) + return -1; + else if (r1.front > r2.front) + return 1; + } + // arrays are equal until here, but it could be that one of them is shorter + if (r1.empty && r2.empty) + return 0; + return r1.empty ? -1 : 1; + } + + /// + static if (is(T == int)) + @safe unittest + { + auto arr1 = Array!int(1, 2); + auto arr2 = Array!int(1, 2); + auto arr3 = Array!int(2, 3); + auto arr4 = Array!int(0, 3); + assert(arr1 <= arr2); + assert(arr2 >= arr1); + assert(arr1 < arr3); + assert(arr3 > arr1); + assert(arr4 < arr1); + assert(arr4 < arr3); + assert(arr3 > arr4); + } + + static if (is(T == int)) + @safe unittest + { + auto arr1 = Array!int(1, 2); + auto arr2 = [1, 2]; + auto arr3 = Array!int(2, 3); + auto arr4 = [0, 3]; + assert(arr1 <= arr2); + assert(arr2 >= arr1); + assert(arr1 < arr3); + assert(arr3 > arr1); + assert(arr4 < arr1); + assert(arr4 < arr3); + assert(arr3 > arr4); + } + + /// + auto toHash() + { + // will be safe with 2.082 + return () @trusted { return _support.hashOf; }(); + } + + /// + @safe unittest + { + auto arr1 = Array!int(1, 2); + assert(arr1.toHash == Array!int(1, 2).toHash); + arr1 ~= 3; + assert(arr1.toHash == Array!int(1, 2, 3).toHash); + assert(Array!int().toHash == Array!int().toHash); + } +} + +version(unittest) private nothrow pure @safe +void testConcatAndAppend() +{ + auto a = Array!(int)(1, 2, 3); + Array!(int) a2 = Array!(int)(); + + auto a3 = a ~ a2; + assert(equal(a3, [1, 2, 3])); + + auto a4 = a3; + a3 = a3 ~ 4; + assert(equal(a3, [1, 2, 3, 4])); + a3 = a3 ~ [5]; + assert(equal(a3, [1, 2, 3, 4, 5])); + assert(equal(a4, [1, 2, 3])); + + a4 = a3; + a3 ~= 6; + assert(equal(a3, [1, 2, 3, 4, 5, 6])); + a3 ~= [7]; + + a3 ~= a3; + assert(equal(a3, [1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7])); + + Array!int a5 = Array!(int)(); + a5 ~= [1, 2, 3]; + assert(equal(a5, [1, 2, 3])); + auto a6 = a5; + a5 = a5; + a5[0] = 10; + assert(equal(a5, a6)); + + // Test concat with mixed qualifiers + auto a7 = immutable Array!(int)(a5); + assert(a7.front == 10); + a5.front = 1; + assert(a7.front == 10); + auto a8 = a5 ~ a7; + assert(equal(a8, [1, 2, 3, 10, 2, 3])); + + auto a9 = const Array!(int)(a5); + auto a10 = a5 ~ a9; + assert(equal(a10, [1, 2, 3, 1, 2, 3])); +} + +@safe unittest +{ + () nothrow pure @safe { + testConcatAndAppend(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} + +version(unittest) private nothrow pure @safe +void testSimple() +{ + auto a = Array!int(); + assert(a.empty); + assert(a.isUnique); + + size_t pos = 0; + a.insert(pos, 1, 2, 3); + assert(a.front == 1); + assert(equal(a, a)); + assert(equal(a, [1, 2, 3])); + + a.popFront(); + assert(a.front == 2); + assert(equal(a, [2, 3])); + + a.insert(pos, [4, 5, 6]); + a.insert(pos, 7); + a.insert(pos, [8]); + assert(equal(a, [8, 7, 4, 5, 6, 2, 3])); + + a.insert(a.length, 0, 1); + a.insert(a.length, [-1, -2]); + assert(equal(a, [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); + + a.front = 9; + assert(equal(a, [9, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); + + auto aTail = a.tail; + assert(aTail.front == 7); + aTail.front = 8; + assert(aTail.front == 8); + assert(a.tail.front == 8); + assert(!a.isUnique); +} + +@safe unittest +{ + () nothrow pure @safe { + testSimple(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} + +version(unittest) private nothrow pure @safe +void testSimpleImmutable() +{ + auto a = Array!(immutable int)(); + assert(a.empty); + + size_t pos = 0; + a.insert(pos, 1, 2, 3); + assert(a.front == 1); + assert(equal(a, a)); + assert(equal(a, [1, 2, 3])); + + a.popFront(); + assert(a.front == 2); + assert(equal(a, [2, 3])); + assert(a.tail.front == 3); + + a.insert(pos, [4, 5, 6]); + a.insert(pos, 7); + a.insert(pos, [8]); + assert(equal(a, [8, 7, 4, 5, 6, 2, 3])); + + a.insert(a.length, 0, 1); + a.insert(a.length, [-1, -2]); + assert(equal(a, [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); + + // Cannot modify immutable values + static assert(!__traits(compiles, a.front = 9)); +} + +@safe unittest +{ + () nothrow pure @safe { + testSimpleImmutable(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} + +version(unittest) private nothrow pure @safe +void testCopyAndRef() +{ + auto aFromList = Array!int(1, 2, 3); + auto aFromRange = Array!int(aFromList); + assert(equal(aFromList, aFromRange)); + + aFromList.popFront(); + assert(equal(aFromList, [2, 3])); + assert(equal(aFromRange, [1, 2, 3])); + + size_t pos = 0; + Array!int aInsFromRange = Array!int(); + aInsFromRange.insert(pos, aFromList); + aFromList.popFront(); + assert(equal(aFromList, [3])); + assert(equal(aInsFromRange, [2, 3])); + + Array!int aInsBackFromRange = Array!int(); + aInsBackFromRange.insert(pos, aFromList); + aFromList.popFront(); + assert(aFromList.empty); + assert(equal(aInsBackFromRange, [3])); + + auto aFromRef = aInsFromRange; + auto aFromDup = aInsFromRange.dup; + assert(aInsFromRange.front == 2); + aFromRef.front = 5; + assert(aInsFromRange.front == 5); + assert(aFromDup.front == 2); +} + +@safe unittest +{ + () nothrow pure @safe { + testCopyAndRef(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} + +version(unittest) private nothrow pure @safe +void testImmutability() +{ + auto a = immutable Array!(int)(1, 2, 3); + auto a2 = a; + auto a3 = a2.save(); + + assert(a2.front == 1); + assert(a2[0] == a2.front); + static assert(!__traits(compiles, a2.front = 4)); + static assert(!__traits(compiles, a2.popFront())); + + auto a4 = a2.tail; + assert(a4.front == 2); + static assert(!__traits(compiles, a4 = a4.tail)); + + // Create a mutable copy from an immutable array + auto a5 = a.dup(); + assert(equal(a5, [1, 2, 3])); + assert(a5.front == 1); + a5.front = 2; + assert(a5.front == 2); + assert(a.front == 1); + assert(equal(a5, [2, 2, 3])); + + // Create immtable copies from mutable, const and immutable + { + auto aa = Array!(int)(1, 2, 3); + auto aa2 = aa.idup(); + assert(aa.opCmpPrefix!"=="(aa._support, 1)); + assert(aa2.opCmpPrefix!"=="(aa2._support, 1)); + } + + { + auto aa = const Array!(int)(1, 2, 3); + auto aa2 = aa.idup(); + assert(aa.opCmpPrefix!"=="(aa._support, 1)); + assert(aa2.opCmpPrefix!"=="(aa2._support, 1)); + } + + { + auto aa = immutable Array!(int)(1, 2, 3); + auto aa2 = aa.idup(); + assert(aa.opCmpPrefix!"=="(aa._support, 2)); + assert(aa2.opCmpPrefix!"=="(aa2._support, 2)); + } +} + +version(unittest) private nothrow pure @safe +void testConstness() +{ + auto a = const Array!(int)(1, 2, 3); + auto a2 = a; + auto a3 = a2.save(); + immutable Array!int a5 = a; + assert(a5.opCmpPrefix!"=="(a5._support, 1)); + assert(a.opCmpPrefix!"=="(a._support, 3)); + + assert(a2.front == 1); + assert(a2[0] == a2.front); + static assert(!__traits(compiles, a2.front = 4)); + static assert(!__traits(compiles, a2.popFront())); + + auto a4 = a2.tail; + assert(a4.front == 2); + static assert(!__traits(compiles, a4 = a4.tail)); +} + +@safe unittest +{ + () nothrow pure @safe { + testImmutability(); + testConstness(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} + +version(unittest) private nothrow pure @safe +void testWithStruct() +{ + auto array = Array!int(1, 2, 3); + { + assert(array.opCmpPrefix!"=="(array._support, 1)); + + auto arrayOfArrays = Array!(Array!int)(array); + assert(array.opCmpPrefix!"=="(array._support, 2)); + assert(equal(arrayOfArrays.front, [1, 2, 3])); + arrayOfArrays.front.front = 2; + assert(equal(arrayOfArrays.front, [2, 2, 3])); + assert(equal(arrayOfArrays.front, array)); + static assert(!__traits(compiles, arrayOfArrays.insert(1))); + + auto immArrayOfArrays = immutable Array!(Array!int)(array); + + // immutable is transitive, so it must iterate over array and + // create a copy, and not set a ref + assert(array.opCmpPrefix!"=="(array._support, 2)); + array.front = 3; + assert(immArrayOfArrays.front.front == 2); + assert(immArrayOfArrays.opCmpPrefix!"=="(immArrayOfArrays._support, 1)); + assert(immArrayOfArrays.front.opCmpPrefix!"=="(immArrayOfArrays.front._support, 1)); + static assert(!__traits(compiles, immArrayOfArrays.front.front = 2)); + static assert(!__traits(compiles, immArrayOfArrays.front = array)); + } + assert(array.opCmpPrefix!"=="(array._support, 1)); + assert(equal(array, [3, 2, 3])); +} + +@safe unittest +{ + () nothrow pure @safe { + testWithStruct(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} + +version(unittest) private nothrow pure @safe +void testWithClass() +{ + class MyClass + { + int x; + this(int x) { this.x = x; } + + this(ref typeof(this) rhs) immutable { x = rhs.x; } + + this(const ref typeof(this) rhs) immutable { x = rhs.x; } + } + + MyClass c = new MyClass(10); + { + Array!MyClass a = Array!MyClass(c); + assert(a.front.x == 10); + assert(a.front is c); + a.front.x = 20; + } + assert(c.x == 20); +} + +@safe unittest +{ + () nothrow pure @safe { + testWithClass(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} + +version(unittest) private @nogc nothrow pure @safe +void testOpOverloads() +{ + auto a = Array!int(1, 2, 3, 4); + assert(a[0] == 1); // opIndex + + // opIndexUnary + ++a[0]; + assert(a[0] == 2); + --a[0]; + assert(a[0] == 1); + a[0]++; + assert(a[0] == 2); + a[0]--; + assert(a[0] == 1); + + // opIndexAssign + a[0] = 2; + assert(a[0] == 2); + + // opIndexOpAssign + a[0] /= 2; + assert(a[0] == 1); + a[0] *= 2; + assert(a[0] == 2); + a[0] -= 1; + assert(a[0] == 1); + a[0] += 1; + assert(a[0] == 2); +} + +@safe unittest +{ + () @nogc nothrow pure @safe { + testOpOverloads(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} + +version(unittest) private nothrow pure @safe +void testSlice() +{ + auto a = Array!int(1, 2, 3, 4); + auto b = a[]; + assert(equal(a, b)); + b[1] = 5; + assert(a[1] == 5); + + size_t startPos = 2; + auto c = b[startPos .. $]; + assert(equal(c, [3, 4])); + c[0] = 5; + assert(equal(a, b)); + assert(equal(a, [1, 5, 5, 4])); + assert(a.capacity == b.capacity && b.capacity == c.capacity + startPos); + + c ~= 5; + assert(equal(c, [5, 4, 5])); + assert(equal(a, b)); + assert(equal(a, [1, 5, 5, 4])); +} + +@safe unittest +{ + () nothrow pure @safe { + testSlice(); + }(); + assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); +} From 039483d15abac5ed5a1616efd53a0edc29d9ada9 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 12 Nov 2018 15:05:52 +0000 Subject: [PATCH 02/22] Make length behave as builtin array's --- src/core/experimental/array.d | 44 +++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 7dc613365b..12fe4b15d6 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -682,10 +682,12 @@ version(unittest) /// struct Array(T) { +private: + import core.atomic : atomicOp; - package T[] _payload; - package Unqual!T[] _support; + T[] _payload; + Unqual!T[] _support; version(unittest) { @@ -696,11 +698,8 @@ struct Array(T) alias _sallocator = shared PrefixAllocator.instance; } -private: - static enum double capacityFactor = 3.0 / 2; static enum initCapacity = 3; - bool _isShared; @trusted @@ -809,10 +808,7 @@ private: size_t i = 0; foreach (ref item; } ~ stuff ~ q{) { - //writefln("the type is %s %s %s", T.stringof, typeof(_support).stringof, typeof(_payload).stringof); alias TT = ElementType!(typeof(_payload)); - //pragma(msg, typeof(item).stringof, " TT is ", TT.stringof); - size_t s = i * stateSize!TT; size_t e = (i + 1) * stateSize!TT; void[] tmp = tmpSupport[s .. e]; @@ -916,7 +912,7 @@ public: // Begin Copy Ctors // { - private static enum copyCtorIncRef = q{ + private enum copyCtorIncRef = q{ _payload = rhs._payload; _support = rhs._support; _isShared = rhs._isShared; @@ -1064,19 +1060,24 @@ public: } /** - * Set the length of the array to `len`. `len` must be less than or equal - * to the `capacity` of the array. + * Set the length of the array to `len`. If `len` exceeds the available + * `capacity` of the array, an attempt to extend the array in place is made. + * If extending is not possible, a reallocation will occur; if the new + * length of the array is longer than `len`, the remainder will be default + * initialized. * * Params: * len = a positive integer * - * Complexity: $(BIGOH 1). + * Complexity: $(BIGOH n). */ - @nogc nothrow pure @trusted - void forceLength(size_t len) + void length(size_t len) { - assert(len <= capacity); - _payload = cast(T[])(_support[slackFront .. len]); + if (capacity < len) + { + reserve(len); + } + _payload = (() @trusted => cast(T[])(_support[slackFront .. len]))(); } /// @@ -1084,8 +1085,16 @@ public: @safe unittest { auto a = Array!int(1, 2, 3); - a.forceLength(2); + a.length = 2; assert(a.length == 2); + + auto b = a; + assert(a.capacity < 10); + a.length = 10; // will trigger a reallocation + assert(a.length == 10); + assert(b.length == 2); + a[0] = 20; + assert(a[0] != b[0]); } /** @@ -1178,7 +1187,6 @@ public: if (i < _payload.length) { emplace(&tmpSupport[i], _payload[i]); - //pragma(msg, typeof(&tmpSupport[i]).stringof); } else { From 6642656a1d243329885f66b172457a317f19ba9b Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 12 Nov 2018 15:07:28 +0000 Subject: [PATCH 03/22] Import Unqual from core traits --- src/core/experimental/array.d | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 12fe4b15d6..4e30befd85 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -1,6 +1,8 @@ /// module core.experimental.array; +import core.internal.traits : Unqual; + // { "Imports" from Phobos // Range functions @@ -171,33 +173,6 @@ private template ModifyTypePreservingTQ(alias Modifier, T) enum bool isAggregateType(T) = is(T == struct) || is(T == union) || is(T == class) || is(T == interface); -/** -Removes all qualifiers, if any, from type `T`. - */ -template Unqual(T) -{ - version (none) // Error: recursive alias declaration @@@BUG1308@@@ - { - static if (is(T U == const U)) alias Unqual = Unqual!U; - else static if (is(T U == immutable U)) alias Unqual = Unqual!U; - else static if (is(T U == inout U)) alias Unqual = Unqual!U; - else static if (is(T U == shared U)) alias Unqual = Unqual!U; - else alias Unqual = T; - } - else // workaround - { - static if (is(T U == immutable U)) alias Unqual = U; - else static if (is(T U == shared inout const U)) alias Unqual = U; - else static if (is(T U == shared inout U)) alias Unqual = U; - else static if (is(T U == shared const U)) alias Unqual = U; - else static if (is(T U == shared U)) alias Unqual = U; - else static if (is(T U == inout const U)) alias Unqual = U; - else static if (is(T U == inout U)) alias Unqual = U; - else static if (is(T U == const U)) alias Unqual = U; - else alias Unqual = T; - } -} - /** Is `From` implicitly convertible to `To`? */ From d9c02b07849be1b3b34346dfd5e2ed954ad1844a Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 12 Nov 2018 15:34:10 +0000 Subject: [PATCH 04/22] Rename internal fields --- src/core/experimental/array.d | 294 +++++++++++++++++----------------- 1 file changed, 147 insertions(+), 147 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 4e30befd85..0ce02b9001 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -611,8 +611,8 @@ version(unittest) private alias SCAlloc = shared PrefixAllocator; private alias SSCAlloc = shared PrefixAllocator; - SCAlloc _allocator; - SSCAlloc _sallocator; + SCAlloc localAllocator; + SSCAlloc sharedAllocator; @nogc nothrow pure @trusted void[] pureAllocate(bool isShared, size_t n) @@ -623,10 +623,10 @@ version(unittest) @nogc nothrow @safe void[] _allocate(bool isShared, size_t n) { - return isShared ? _sallocator.allocate(n) : _allocator.allocate(n); + return isShared ? sharedAllocator.allocate(n) : localAllocator.allocate(n); } - static if (hasMember!(typeof(_allocator), "expand")) + static if (hasMember!(typeof(localAllocator), "expand")) { @nogc nothrow pure @trusted bool pureExpand(bool isShared, ref void[] b, size_t delta) @@ -637,7 +637,7 @@ version(unittest) @nogc nothrow @safe bool _expand(bool isShared, ref void[] b, size_t delta) { - return isShared ? _sallocator.expand(b, delta) : _allocator.expand(b, delta); + return isShared ? sharedAllocator.expand(b, delta) : localAllocator.expand(b, delta); } } @@ -650,7 +650,7 @@ version(unittest) @nogc nothrow void _dispose(T)(bool isShared, T[] b) { - return isShared ? _sallocator.dispose(b) : _allocator.dispose(b); + return isShared ? sharedAllocator.dispose(b) : localAllocator.dispose(b); } } @@ -661,87 +661,87 @@ private: import core.atomic : atomicOp; - T[] _payload; - Unqual!T[] _support; + T[] payload; + Unqual!T[] support; version(unittest) { } else { - alias _allocator = shared PrefixAllocator.instance; - alias _sallocator = shared PrefixAllocator.instance; + alias localAllocator = shared PrefixAllocator.instance; + alias sharedAllocator = shared PrefixAllocator.instance; } static enum double capacityFactor = 3.0 / 2; static enum initCapacity = 3; - bool _isShared; + bool isShared; @trusted auto pref() const { - assert(_support !is null); - if (_isShared) + assert(support !is null); + if (isShared) { - return _sallocator.prefix(_support); + return sharedAllocator.prefix(support); } else { - return _allocator.prefix(_support); + return localAllocator.prefix(support); } } - private size_t _opPrefix(string op, T)(const T[] support, size_t val) const + private size_t _opPrefix(string op, T)(const T[] _support, size_t val) const { - assert(support !is null); - if (_isShared) + assert(_support !is null); + if (isShared) { - return cast(size_t)(atomicOp!op(*cast(shared size_t *)&_sallocator.prefix(support), val)); + return cast(size_t)(atomicOp!op(*cast(shared size_t *)&sharedAllocator.prefix(_support), val)); } else { - mixin("return cast(size_t)(*cast(size_t *)&_allocator.prefix(support)" ~ op ~ "val);"); + mixin("return cast(size_t)(*cast(size_t *)&localAllocator.prefix(_support)" ~ op ~ "val);"); } } @nogc nothrow pure @trusted - size_t opPrefix(string op, T)(const T[] support, size_t val) const + size_t opPrefix(string op, T)(const T[] _support, size_t val) const if ((op == "+=") || (op == "-=")) { - return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(support, val); + return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(_support, val); } @nogc nothrow pure @trusted - size_t opCmpPrefix(string op, T)(const T[] support, size_t val) const + size_t opCmpPrefix(string op, T)(const T[] _support, size_t val) const if ((op == "==") || (op == "<=") || (op == "<") || (op == ">=") || (op == ">")) { - return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(support, val); + return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(_support, val); } @nogc nothrow pure @trusted - void addRef(SupportQual, this Q)(SupportQual support) + void addRef(SupportQual, this Q)(SupportQual _support) { - assert(support !is null); - cast(void) opPrefix!("+=")(support, 1); + assert(_support !is null); + cast(void) opPrefix!("+=")(_support, 1); } - void delRef(Unqual!T[] support) + void delRef(Unqual!T[] _support) { // Will be optimized away, but the type system infers T's safety if (0) { T t = T.init; } - assert(support !is null); - if (opPrefix!("-=")(support, 1) == 0) + assert(_support !is null); + if (opPrefix!("-=")(_support, 1) == 0) { () @trusted { version(unittest) { - pureDispose(_isShared, support); + pureDispose(isShared, _support); } else { - _allocator.dispose(support); + localAllocator.dispose(_support); } }(); } @@ -764,18 +764,18 @@ private: version(unittest) { - void[] tmpSupport = (() @trusted => pureAllocate(_isShared, stuffLength * stateSize!T))(); + void[] tmpSupport = (() @trusted => pureAllocate(isShared, stuffLength * stateSize!T))(); } else { void[] tmpSupport; - if (_isShared) + if (isShared) { - tmpSupport = (() @trusted => _sallocator.allocate(stuffLength * stateSize!T))(); + tmpSupport = (() @trusted => sharedAllocator.allocate(stuffLength * stateSize!T))(); } else { - tmpSupport = (() @trusted => _allocator.allocate(stuffLength * stateSize!T))(); + tmpSupport = (() @trusted => localAllocator.allocate(stuffLength * stateSize!T))(); } } @@ -783,7 +783,7 @@ private: size_t i = 0; foreach (ref item; } ~ stuff ~ q{) { - alias TT = ElementType!(typeof(_payload)); + alias TT = ElementType!(typeof(payload)); size_t s = i * stateSize!TT; size_t e = (i + 1) * stateSize!TT; void[] tmp = tmpSupport[s .. e]; @@ -791,17 +791,17 @@ private: (() @trusted => emplace!TT(tmp, item))(); } - _support = (() @trusted => cast(typeof(_support))(tmpSupport))(); - _payload = (() @trusted => cast(typeof(_payload))(_support[0 .. stuffLength]))(); - if (_support) addRef(_support); + support = (() @trusted => cast(typeof(support))(tmpSupport))(); + payload = (() @trusted => cast(typeof(payload))(support[0 .. stuffLength]))(); + if (support) addRef(support); }; } void destroyUnused() { - if (_support !is null) + if (support !is null) { - delRef(_support); + delRef(support); } } @@ -823,7 +823,7 @@ public: { static if (is(Q == immutable) || is(Q == const)) { - _isShared = true; + isShared = true; mixin(immutableInsert!(typeof(values))("values")); } else @@ -875,7 +875,7 @@ public: { static if (is(Q == immutable) || is(Q == const)) { - _isShared = true; + isShared = true; mixin(immutableInsert!(typeof(stuff))("stuff")); } else @@ -888,13 +888,13 @@ public: // { private enum copyCtorIncRef = q{ - _payload = rhs._payload; - _support = rhs._support; - _isShared = rhs._isShared; + payload = rhs.payload; + support = rhs.support; + isShared = rhs.isShared; - if (_support !is null) + if (support !is null) { - addRef(_support); + addRef(support); } }; @@ -925,14 +925,14 @@ public: this(ref typeof(this) rhs) immutable { - _isShared = true; + isShared = true; mixin(immutableInsert!(typeof(rhs))("rhs")); } this(const ref typeof(this) rhs) immutable { - _isShared = rhs._isShared; - mixin(immutableInsert!(typeof(rhs._payload))("rhs._payload")); + isShared = rhs.isShared; + mixin(immutableInsert!(typeof(rhs.payload))("rhs.payload")); } this(immutable ref typeof(this) rhs) immutable @@ -946,15 +946,15 @@ public: // End Copy Ctors // Immutable ctors - private this(SuppQual, PaylQual, this Qualified)(SuppQual support, PaylQual payload, bool isShared) - if (is(typeof(_support) : typeof(support))) + private this(SuppQual, PaylQual, this Qualified)(SuppQual _support, PaylQual _payload, bool _isShared) + if (is(typeof(support) : typeof(_support))) { - _support = support; - _payload = payload; - _isShared = isShared; - if (_support !is null) + support = _support; + payload = _payload; + isShared = _isShared; + if (support !is null) { - addRef(_support); + addRef(support); } } @@ -999,13 +999,13 @@ public: private @nogc nothrow pure @trusted size_t slackFront() const { - return _payload.ptr - _support.ptr; + return payload.ptr - support.ptr; } private @nogc nothrow pure @trusted size_t slackBack() const { - return _support.ptr + _support.length - _payload.ptr - _payload.length; + return support.ptr + support.length - payload.ptr - payload.length; } /** @@ -1019,7 +1019,7 @@ public: @nogc nothrow pure @safe size_t length() const { - return _payload.length; + return payload.length; } /// ditto @@ -1052,7 +1052,7 @@ public: { reserve(len); } - _payload = (() @trusted => cast(T[])(_support[slackFront .. len]))(); + payload = (() @trusted => cast(T[])(support[slackFront .. len]))(); } /// @@ -1117,28 +1117,28 @@ public: if (n <= capacity) { return; } - static if (hasMember!(typeof(_allocator), "expand")) - if (_support && opCmpPrefix!"=="(_support, 1)) + static if (hasMember!(typeof(localAllocator), "expand")) + if (support && opCmpPrefix!"=="(support, 1)) { - void[] buf = _support; + void[] buf = support; version(unittest) { - auto successfulExpand = pureExpand(_isShared, buf, (n - capacity) * stateSize!T); + auto successfulExpand = pureExpand(isShared, buf, (n - capacity) * stateSize!T); } else { - auto successfulExpand = _allocator.expand(buf, (n - capacity) * stateSize!T); + auto successfulExpand = localAllocator.expand(buf, (n - capacity) * stateSize!T); } if (successfulExpand) { - const oldLength = _support.length; - _support = (() @trusted => cast(Unqual!T[])(buf))(); + const oldLength = support.length; + support = (() @trusted => cast(Unqual!T[])(buf))(); // Emplace extended buf // TODO: maybe? emplace only if T has indirections - foreach (i; oldLength .. _support.length) + foreach (i; oldLength .. support.length) { - emplace(&_support[i]); + emplace(&support[i]); } return; } @@ -1150,18 +1150,18 @@ public: version(unittest) { - auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(_isShared, n * stateSize!T)))(); + auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(isShared, n * stateSize!T)))(); } else { - auto tmpSupport = (() @trusted => cast(Unqual!T[])(_allocator.allocate(n * stateSize!T)))(); + auto tmpSupport = (() @trusted => cast(Unqual!T[])(localAllocator.allocate(n * stateSize!T)))(); } assert(tmpSupport !is null); for (size_t i = 0; i < tmpSupport.length; ++i) { - if (i < _payload.length) + if (i < payload.length) { - emplace(&tmpSupport[i], _payload[i]); + emplace(&tmpSupport[i], payload[i]); } else { @@ -1170,9 +1170,9 @@ public: } destroyUnused(); - _support = tmpSupport; - addRef(_support); - _payload = (() @trusted => cast(T[])(_support[0 .. _payload.length]))(); + support = tmpSupport; + addRef(support); + payload = (() @trusted => cast(T[])(support[0 .. payload.length]))(); assert(capacity >= n); } @@ -1228,11 +1228,11 @@ public: version(unittest) { - auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(_isShared, stuffLength * stateSize!T)))(); + auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(isShared, stuffLength * stateSize!T)))(); } else { - auto tmpSupport = (() @trusted => cast(Unqual!T[])(_allocator.allocate(stuffLength * stateSize!T)))(); + auto tmpSupport = (() @trusted => cast(Unqual!T[])(localAllocator.allocate(stuffLength * stateSize!T)))(); } assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is null)); for (size_t i = 0; i < tmpSupport.length; ++i) @@ -1248,11 +1248,11 @@ public: size_t result = insert(pos, tmpSupport); version(unittest) { - () @trusted { pureDispose(_isShared, tmpSupport); }(); + () @trusted { pureDispose(isShared, tmpSupport); }(); } else { - () @trusted { _allocator.dispose(tmpSupport); }(); + () @trusted { localAllocator.dispose(tmpSupport); }(); } return result; } @@ -1264,7 +1264,7 @@ public: // Will be optimized away, but the type system infers T's safety if (0) { T t = T.init; } - assert(pos <= _payload.length); + assert(pos <= payload.length); if (stuff.length == 0) return 0; if (stuff.length > slackBack) @@ -1276,23 +1276,23 @@ public: } reserve((() @trusted => cast(size_t)(newCapacity))()); } - //_support[pos + stuff.length .. _payload.length + stuff.length] = - //_support[pos .. _payload.length]; - for (size_t i = _payload.length + stuff.length - 1; i >= pos + + //support[pos + stuff.length .. payload.length + stuff.length] = + //support[pos .. payload.length]; + for (size_t i = payload.length + stuff.length - 1; i >= pos + stuff.length; --i) { // Avoids underflow if payload is empty - _support[i] = _support[i - stuff.length]; + support[i] = support[i - stuff.length]; } - //writefln("typeof support[i] %s", typeof(_support[0]).stringof); + //writefln("typeof support[i] %s", typeof(support[0]).stringof); - //_support[pos .. pos + stuff.length] = stuff[]; + //support[pos .. pos + stuff.length] = stuff[]; for (size_t i = pos, j = 0; i < pos + stuff.length; ++i, ++j) { - _support[i] = stuff[j]; + support[i] = stuff[j]; } - _payload = (() @trusted => cast(T[])(_support[0 .. _payload.length + stuff.length]))(); + payload = (() @trusted => cast(T[])(support[0 .. payload.length + stuff.length]))(); return stuff.length; } @@ -1321,9 +1321,9 @@ public: @nogc nothrow pure @safe bool isUnique(this _)() { - if (_support !is null) + if (support !is null) { - return cast(bool) opCmpPrefix!"=="(_support, 1); + return cast(bool) opCmpPrefix!"=="(support, 1); } return true; } @@ -1381,7 +1381,7 @@ public: ref auto front(this _)() { assert(!empty, "Array.front: Array is empty"); - return _payload[0]; + return payload[0]; } /// @@ -1404,7 +1404,7 @@ public: void popFront() { assert(!empty, "Array.popFront: Array is empty"); - _payload = _payload[1 .. $]; + payload = payload[1 .. $]; } /// @@ -1486,7 +1486,7 @@ public: // Iterate through the underlying payload // The array is kept alive (rc > 0) from the caller scope - foreach (ref e; this._payload) + foreach (ref e; this.payload) { static if (!is(typeof(fn(T.init)) == int)) { @@ -1527,13 +1527,13 @@ public: assert(ia.each!bar == false); } - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } //int opApply(int delegate(const ref T) dg) const //{ - //if (_payload.length && dg(_payload[0])) return 1; + //if (payload.length && dg(payload[0])) return 1; //if (!this.empty) this.tail.opApply(dg); //return 0; //} @@ -1687,7 +1687,7 @@ public: } body { - return typeof(this)(_support, _payload[start .. end], _isShared); + return typeof(this)(support, payload[start .. end], isShared); } /// @@ -1719,7 +1719,7 @@ public: } body { - return _payload[idx]; + return payload[idx]; } /// @@ -1749,7 +1749,7 @@ public: } body { - mixin("return " ~ op ~ "_payload[idx];"); + mixin("return " ~ op ~ "payload[idx];"); } /// @@ -1783,7 +1783,7 @@ public: } body { - return _payload[idx] = elem; + return payload[idx] = elem; } /// @@ -1812,7 +1812,7 @@ public: if (isImplicitlyConvertible!(U, T)) body { - _payload[] = elem; + payload[] = elem; return this; } @@ -1847,7 +1847,7 @@ public: } body { - return _payload[start .. end] = elem; + return payload[start .. end] = elem; } /// @@ -1881,7 +1881,7 @@ public: } body { - mixin("return _payload[idx]" ~ op ~ "= elem;"); + mixin("return payload[idx]" ~ op ~ "= elem;"); } /// @@ -1963,19 +1963,19 @@ public: */ auto ref opAssign()(auto ref typeof(this) rhs) { - if (rhs._support !is null && _support is rhs._support) + if (rhs.support !is null && support is rhs.support) { - if (rhs._payload is _payload) + if (rhs.payload is payload) return this; } - if (rhs._support !is null) + if (rhs.support !is null) { - rhs.addRef(rhs._support); + rhs.addRef(rhs.support); } destroyUnused(); - _support = rhs._support; - _payload = rhs._payload; + support = rhs.support; + payload = rhs.payload; return this; } @@ -2051,7 +2051,7 @@ public: /// bool opEquals()(auto ref typeof(this) rhs) const { - return _support.equal(rhs); + return support.equal(rhs); } /// @@ -2125,7 +2125,7 @@ public: auto toHash() { // will be safe with 2.082 - return () @trusted { return _support.hashOf; }(); + return () @trusted { return support.hashOf; }(); } /// @@ -2189,8 +2189,8 @@ void testConcatAndAppend() () nothrow pure @safe { testConcatAndAppend(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } version(unittest) private nothrow pure @safe @@ -2235,8 +2235,8 @@ void testSimple() () nothrow pure @safe { testSimple(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } version(unittest) private nothrow pure @safe @@ -2274,8 +2274,8 @@ void testSimpleImmutable() () nothrow pure @safe { testSimpleImmutable(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } version(unittest) private nothrow pure @safe @@ -2315,8 +2315,8 @@ void testCopyAndRef() () nothrow pure @safe { testCopyAndRef(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } version(unittest) private nothrow pure @safe @@ -2348,22 +2348,22 @@ void testImmutability() { auto aa = Array!(int)(1, 2, 3); auto aa2 = aa.idup(); - assert(aa.opCmpPrefix!"=="(aa._support, 1)); - assert(aa2.opCmpPrefix!"=="(aa2._support, 1)); + assert(aa.opCmpPrefix!"=="(aa.support, 1)); + assert(aa2.opCmpPrefix!"=="(aa2.support, 1)); } { auto aa = const Array!(int)(1, 2, 3); auto aa2 = aa.idup(); - assert(aa.opCmpPrefix!"=="(aa._support, 1)); - assert(aa2.opCmpPrefix!"=="(aa2._support, 1)); + assert(aa.opCmpPrefix!"=="(aa.support, 1)); + assert(aa2.opCmpPrefix!"=="(aa2.support, 1)); } { auto aa = immutable Array!(int)(1, 2, 3); auto aa2 = aa.idup(); - assert(aa.opCmpPrefix!"=="(aa._support, 2)); - assert(aa2.opCmpPrefix!"=="(aa2._support, 2)); + assert(aa.opCmpPrefix!"=="(aa.support, 2)); + assert(aa2.opCmpPrefix!"=="(aa2.support, 2)); } } @@ -2374,8 +2374,8 @@ void testConstness() auto a2 = a; auto a3 = a2.save(); immutable Array!int a5 = a; - assert(a5.opCmpPrefix!"=="(a5._support, 1)); - assert(a.opCmpPrefix!"=="(a._support, 3)); + assert(a5.opCmpPrefix!"=="(a5.support, 1)); + assert(a.opCmpPrefix!"=="(a.support, 3)); assert(a2.front == 1); assert(a2[0] == a2.front); @@ -2393,8 +2393,8 @@ void testConstness() testImmutability(); testConstness(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } version(unittest) private nothrow pure @safe @@ -2402,10 +2402,10 @@ void testWithStruct() { auto array = Array!int(1, 2, 3); { - assert(array.opCmpPrefix!"=="(array._support, 1)); + assert(array.opCmpPrefix!"=="(array.support, 1)); auto arrayOfArrays = Array!(Array!int)(array); - assert(array.opCmpPrefix!"=="(array._support, 2)); + assert(array.opCmpPrefix!"=="(array.support, 2)); assert(equal(arrayOfArrays.front, [1, 2, 3])); arrayOfArrays.front.front = 2; assert(equal(arrayOfArrays.front, [2, 2, 3])); @@ -2416,15 +2416,15 @@ void testWithStruct() // immutable is transitive, so it must iterate over array and // create a copy, and not set a ref - assert(array.opCmpPrefix!"=="(array._support, 2)); + assert(array.opCmpPrefix!"=="(array.support, 2)); array.front = 3; assert(immArrayOfArrays.front.front == 2); - assert(immArrayOfArrays.opCmpPrefix!"=="(immArrayOfArrays._support, 1)); - assert(immArrayOfArrays.front.opCmpPrefix!"=="(immArrayOfArrays.front._support, 1)); + assert(immArrayOfArrays.opCmpPrefix!"=="(immArrayOfArrays.support, 1)); + assert(immArrayOfArrays.front.opCmpPrefix!"=="(immArrayOfArrays.front.support, 1)); static assert(!__traits(compiles, immArrayOfArrays.front.front = 2)); static assert(!__traits(compiles, immArrayOfArrays.front = array)); } - assert(array.opCmpPrefix!"=="(array._support, 1)); + assert(array.opCmpPrefix!"=="(array.support, 1)); assert(equal(array, [3, 2, 3])); } @@ -2433,8 +2433,8 @@ void testWithStruct() () nothrow pure @safe { testWithStruct(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } version(unittest) private nothrow pure @safe @@ -2465,8 +2465,8 @@ void testWithClass() () nothrow pure @safe { testWithClass(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } version(unittest) private @nogc nothrow pure @safe @@ -2505,8 +2505,8 @@ void testOpOverloads() () @nogc nothrow pure @safe { testOpOverloads(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } version(unittest) private nothrow pure @safe @@ -2537,6 +2537,6 @@ void testSlice() () nothrow pure @safe { testSlice(); }(); - assert(_allocator.bytesUsed == 0, "Array ref count leaks memory"); - assert(_sallocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); + assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } From 89b48a673e3e45d56875be1e9edb5b5d159bac45 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 12 Nov 2018 22:01:55 +0000 Subject: [PATCH 05/22] Remove range deps --- src/core/experimental/array.d | 347 +++++----------------------------- 1 file changed, 52 insertions(+), 295 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 0ce02b9001..b3e9ed2d6a 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -5,207 +5,6 @@ import core.internal.traits : Unqual; // { "Imports" from Phobos -// Range functions - -/** -Implements the range interface primitive `front` for built-in arrays. Due to the -fact that nonmember functions can be called with the first argument using the dot -notation, `array.front` is equivalent to `front(array)`. For $(GLOSSARY narrow strings), -`front` automatically returns the first $(GLOSSARY code point) as _a `dchar`. -*/ -@property ref T front(T)(T[] a) @safe pure nothrow @nogc -//TODO: if (!isNarrowString!(T[]) && !is(T[] == void[])) -if (!is(T[] == void[])) -{ - assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); - return a[0]; -} - -/** -Implements the range interface primitive `empty` for types that obey `hasLength` -property and for narrow strings. - */ -@property bool empty(T)(auto ref scope const(T) a) -//TODO: if (is(typeof(a.length) : size_t) || isNarrowString!T) -if (is(typeof(a.length) : size_t)) -{ - return !a.length; -} - -/** -Implements the range interface primitive `popFront` for built-in arrays. For -$(GLOSSARY narrow strings), `popFront` automatically advances to the next -$(GLOSSARY code point). -*/ -void popFront(T)(ref T[] a) @safe pure nothrow @nogc -//TODO: if (!isNarrowString!(T[]) && !is(T[] == void[])) -if (!is(T[] == void[])) -{ - assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof); - a = a[1 .. $]; -} - -/** -This is a best-effort implementation of `length` for any kind of range. -*/ -auto walkLength(Range)(Range range) -if (isInputRange!Range && !isInfinite!Range) -{ - static if (hasLength!Range) - return range.length; - else - { - size_t result; - for ( ; !range.empty ; range.popFront() ) - ++result; - return result; - } -} - -/** -*/ -enum bool isInputRange(R) = - is(typeof(R.init) == R) - && is(typeof(R.init.empty()) == bool) - && is(typeof((return ref R r) => r.front)) - && !is(typeof(R.init.front()) == void) - && is(typeof((R r) => r.popFront)); - -/** -*/ -template isInfinite(R) -{ - static if (isInputRange!R && __traits(compiles, { enum e = R.empty; })) - enum bool isInfinite = !R.empty; - else - enum bool isInfinite = false; -} - -// End Range functions - -/** -Yields `true` if and only if `T` is an aggregate that defines a symbol called `name`. - */ -enum hasMember(T, string name) = __traits(hasMember, T, name); - -/** -Detect whether type `T` is an array (static or dynamic. -*/ -enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; - -/** - * Detect whether type `T` is a static array. - */ -enum bool isStaticArray(T) = __traits(isStaticArray, T); - -/** - * Detect whether type `T` is a dynamic array. - */ -enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T; - -/* - */ -template DynamicArrayTypeOf(T) -{ - static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT)) - alias X = DynamicArrayTypeOf!AT; - else - alias X = OriginalType!T; - - static if (is(Unqual!X : E[], E) && !is(typeof({ enum n = X.length; }))) - { - alias DynamicArrayTypeOf = X; - } - else - static assert(0, T.stringof~" is not a dynamic array"); -} - -// SomethingTypeOf -private template AliasThisTypeOf(T) -if (isAggregateType!T) -{ - alias members = AliasSeq!(__traits(getAliasThis, T)); - - static if (members.length == 1) - { - alias AliasThisTypeOf = typeof(__traits(getMember, T.init, members[0])); - } - else - static assert(0, T.stringof~" does not have alias this type"); -} - -template AliasSeq(TList...) -{ - alias AliasSeq = TList; -} - -/** - * Strips off all `enum`s from type `T`. - */ -template OriginalType(T) -{ - template Impl(T) - { - static if (is(T U == enum)) alias Impl = OriginalType!U; - else alias Impl = T; - } - - alias OriginalType = ModifyTypePreservingTQ!(Impl, T); -} - -// [For internal use] -private template ModifyTypePreservingTQ(alias Modifier, T) -{ - static if (is(T U == immutable U)) alias ModifyTypePreservingTQ = immutable Modifier!U; - else static if (is(T U == shared inout const U)) alias ModifyTypePreservingTQ = shared inout const Modifier!U; - else static if (is(T U == shared inout U)) alias ModifyTypePreservingTQ = shared inout Modifier!U; - else static if (is(T U == shared const U)) alias ModifyTypePreservingTQ = shared const Modifier!U; - else static if (is(T U == shared U)) alias ModifyTypePreservingTQ = shared Modifier!U; - else static if (is(T U == inout const U)) alias ModifyTypePreservingTQ = inout const Modifier!U; - else static if (is(T U == inout U)) alias ModifyTypePreservingTQ = inout Modifier!U; - else static if (is(T U == const U)) alias ModifyTypePreservingTQ = const Modifier!U; - else alias ModifyTypePreservingTQ = Modifier!T; -} - -/** - * Detect whether type `T` is an aggregate type. - */ -enum bool isAggregateType(T) = is(T == struct) || is(T == union) || - is(T == class) || is(T == interface); - -/** -Is `From` implicitly convertible to `To`? - */ -enum bool isImplicitlyConvertible(From, To) = is(From : To); - -/** -The element type of `R`. `R` does not have to be a range. The element type is -determined as the type yielded by `r.front` for an object `r` of type `R`. For -example, `ElementType!(T[])` is `T` if `T[]` isn't a narrow string; if it is, the -element type is `dchar`. If `R` doesn't have `front`, `ElementType!R` is `void`. - */ -template ElementType(R) -{ - static if (is(typeof(R.init.front.init) T)) - alias ElementType = T; - else - alias ElementType = void; -} - -/** -Yields `true` if `R` has a `length` member that returns a value of `size_t` -type. `R` does not have to be a range. If `R` is a range, algorithms in the -standard library are only guaranteed to support `length` with type `size_t`. -*/ -template hasLength(R) -{ - static if (is(typeof(((R* r) => r.length)(null)) Length)) - enum bool hasLength = is(Length == size_t); - //TODO: enum bool hasLength = is(Length == size_t) && !isNarrowString!R; - else - enum bool hasLength = false; -} - // { Allocators /** @@ -457,8 +256,20 @@ void dispose(A, T)(auto ref A alloc, auto ref T[] array) // } End "Imports" from Phobos +/** +The element type of `R`. `R` does not have to be a range. The element type is +determined as the type yielded by `r[0]` for an object `r` of type `R`. + */ +template ElementType(R) +{ + static if (is(typeof(R.init[0].init) T)) + alias ElementType = T; + else + alias ElementType = void; +} + auto tail(Collection)(Collection collection) -if (isInputRange!Collection) +if (is(typeof(collection.popFront()))) { collection.popFront(); return collection; @@ -471,11 +282,11 @@ if (is(ElementType!T : ElementType!U) { if (a.length != b.length) return false; - while (!a.empty) + while (a.length) { - if (a.front != b.front) return false; - a.popFront(); - b.popFront(); + if (a[0] != b[0]) return false; + a = a[1 .. $]; + b = b[1 .. $]; } return true; } @@ -626,7 +437,7 @@ version(unittest) return isShared ? sharedAllocator.allocate(n) : localAllocator.allocate(n); } - static if (hasMember!(typeof(localAllocator), "expand")) + static if (__traits(hasMember, typeof(localAllocator), "expand")) { @nogc nothrow pure @trusted bool pureExpand(bool isShared, ref void[] b, size_t delta) @@ -749,16 +560,8 @@ private: static string immutableInsert(StuffType)(string stuff) { - static if (hasLength!StuffType) - { - auto stuffLengthStr = q{ - size_t stuffLength = } ~ stuff ~ ".length;"; - } - else - { - auto stuffLengthStr = q{ - size_t stuffLength = walkLength(} ~ stuff ~ ");"; - } + auto stuffLengthStr = q{ + size_t stuffLength = } ~ stuff ~ ".length;"; return stuffLengthStr ~ q{ @@ -816,10 +619,8 @@ public: * * Complexity: $(BIGOH m), where `m` is the number of items. */ - //version(none) this(U, this Q)(U[] values...) - if (!is(Q == shared) - && isImplicitlyConvertible!(U, T)) + if (!is(Q == shared) && is(U : T)) { static if (is(Q == immutable) || is(Q == const)) { @@ -867,6 +668,7 @@ public: * * Complexity: $(BIGOH m), where `m` is the number of elements in the range. */ + version(none) this(Stuff, this Q)(Stuff stuff) if (!is(Q == shared) && isInputRange!Stuff && !isInfinite!Stuff @@ -1117,7 +919,7 @@ public: if (n <= capacity) { return; } - static if (hasMember!(typeof(localAllocator), "expand")) + static if (__traits(hasMember, typeof(localAllocator), "expand")) if (support && opCmpPrefix!"=="(support, 1)) { void[] buf = support; @@ -1188,20 +990,13 @@ public: } /** - * Inserts the elements of an - * $(REF_ALTTEXT input range, isInputRange, std,range,primitives), or a - * variable number of items, at the given `pos`. - * - * If no allocator was provided when the array was created, the - * $(REF, GCAllocator, std,experimental,allocator,gc_allocator) will be used. - * If `Stuff` defines `length`, `Array` will use it to reserve the - * necessary amount of memory. + * Inserts the elements of an array, or a built-in array or an element + * at the given `pos`. * * Params: * pos = a positive integer - * stuff = an input range of elements that are implitictly convertible - * to `T`; a variable number of items either in the form of a - * list or as a built-in array + * stuff = an element, or an array, or built-in array of elements that + * are implitictly convertible to `T` * * Returns: * the number of elements inserted @@ -1210,57 +1005,18 @@ public: * elements in the range. */ size_t insert(Stuff)(size_t pos, Stuff stuff) - if (!isArray!(typeof(stuff)) && isInputRange!Stuff && !isInfinite!Stuff - && isImplicitlyConvertible!(ElementType!Stuff, T)) + if (is(Stuff == Array!T)) { - // Will be optimized away, but the type system infers T's safety - if (0) { T t = T.init; } - - static if (hasLength!Stuff) - { - size_t stuffLength = stuff.length; - } - else - { - size_t stuffLength = walkLength(stuff); - } - if (stuffLength == 0) return 0; - - version(unittest) - { - auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(isShared, stuffLength * stateSize!T)))(); - } - else - { - auto tmpSupport = (() @trusted => cast(Unqual!T[])(localAllocator.allocate(stuffLength * stateSize!T)))(); - } - assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is null)); - for (size_t i = 0; i < tmpSupport.length; ++i) - { - emplace(&tmpSupport[i]); - } - - size_t i = 0; - foreach (ref item; stuff) - { - tmpSupport[i++] = item; - } - size_t result = insert(pos, tmpSupport); - version(unittest) - { - () @trusted { pureDispose(isShared, tmpSupport); }(); - } - else - { - () @trusted { localAllocator.dispose(tmpSupport); }(); - } - return result; + mixin(insertImpl); } - /// ditto - size_t insert(Stuff)(size_t pos, Stuff[] stuff...) - if (isImplicitlyConvertible!(Stuff, T)) + size_t insert(U)(size_t pos, U[] stuff...) + if (is(U : T)) { + mixin(insertImpl); + } + + private enum insertImpl = q{ // Will be optimized away, but the type system infers T's safety if (0) { T t = T.init; } @@ -1285,8 +1041,7 @@ public: support[i] = support[i - stuff.length]; } - //writefln("typeof support[i] %s", typeof(support[0]).stringof); - + // Can't use below, because it doesn't do opAssign, but memcpy //support[pos .. pos + stuff.length] = stuff[]; for (size_t i = pos, j = 0; i < pos + stuff.length; ++i, ++j) { support[i] = stuff[j]; @@ -1294,7 +1049,7 @@ public: payload = (() @trusted => cast(T[])(support[0 .. payload.length + stuff.length]))(); return stuff.length; - } + }; /// static if (is(T == int)) @@ -1776,7 +1531,7 @@ public: * Complexity: $(BIGOH 1). */ ref auto opIndexAssign(U)(U elem, size_t idx) - if (isImplicitlyConvertible!(U, T)) + if (is(U : T)) in { assert(idx < length, "Array.opIndexAssign: Index out of bounds"); @@ -1809,7 +1564,7 @@ public: Complexity: $(BIGOH n). */ ref auto opIndexAssign(U)(U elem) - if (isImplicitlyConvertible!(U, T)) + if (is(U : T)) body { payload[] = elem; @@ -1839,7 +1594,7 @@ public: Complexity: $(BIGOH n). */ auto opSliceAssign(U)(U elem, size_t start, size_t end) - if (isImplicitlyConvertible!(U, T)) + if (is(U : T)) in { assert(start <= end, "Array.opSliceAssign: Index out of bounds"); @@ -1874,7 +1629,7 @@ public: * Complexity: $(BIGOH 1). */ ref auto opIndexOpAssign(string op, U)(U elem, size_t idx) - if (isImplicitlyConvertible!(U, T)) + if (is(U : T)) in { assert(idx < length, "Array.opIndexOpAssign!" ~ op ~ ": Index out of bounds"); @@ -1912,7 +1667,7 @@ public: if (op == "~" && (is (U : const typeof(this)) || is (U : T) - || (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T)) + || (is (U == V[], V) && is(V : T)) )) { auto newArray = this.dup(); @@ -2022,7 +1777,7 @@ public: if (op == "~" && (is (U == typeof(this)) || is (U : T) - || (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T)) + || (is (U == V[], V) && is(V : T)) )) { insert(length, rhs); @@ -2051,7 +1806,7 @@ public: /// bool opEquals()(auto ref typeof(this) rhs) const { - return support.equal(rhs); + return payload.equal(rhs); } /// @@ -2071,21 +1826,23 @@ public: /// int opCmp(U)(auto ref U rhs) - if (isInputRange!U && isImplicitlyConvertible!(ElementType!U, T)) + if ((is(U == Array!V, V) || is(U == V[], V)) && is(V : T)) { auto r1 = this; auto r2 = rhs; - for (; !r1.empty && !r2.empty; r1.popFront, r2.popFront) + while (r1.length && r2.length) { - if (r1.front < r2.front) + if (r1[0] < r2[0]) return -1; - else if (r1.front > r2.front) + else if (r1[0] > r2[0]) return 1; + r1 = r1[1 .. $]; + r2 = r2[1 .. $]; } // arrays are equal until here, but it could be that one of them is shorter - if (r1.empty && r2.empty) + if (!r1.length && !r2.length) return 0; - return r1.empty ? -1 : 1; + return (r1.length == 0) ? -1 : 1; } /// From 815942b004b51d2eefd29be06daf32e58c0878bb Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 20 Nov 2018 14:23:22 +0200 Subject: [PATCH 06/22] Remove obsolete ctor --- src/core/experimental/array.d | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index b3e9ed2d6a..52c4d56bf1 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -655,37 +655,6 @@ public: } } - /** - * Constructs a qualified array out of an - * $(REF_ALTTEXT input range, isInputRange, std,range,primitives) - * that will use the collection decided allocator object. - * If `Stuff` defines `length`, `Array` will use it to reserve the - * necessary amount of memory. - * - * Params: - * stuff = an input range of elements that are implitictly convertible - * to `T` - * - * Complexity: $(BIGOH m), where `m` is the number of elements in the range. - */ - version(none) - this(Stuff, this Q)(Stuff stuff) - if (!is(Q == shared) - && isInputRange!Stuff && !isInfinite!Stuff - && isImplicitlyConvertible!(ElementType!Stuff, T) - && !is(Stuff == T[])) - { - static if (is(Q == immutable) || is(Q == const)) - { - isShared = true; - mixin(immutableInsert!(typeof(stuff))("stuff")); - } - else - { - insert(0, stuff); - } - } - // Begin Copy Ctors // { From 60b3143df655dbaf6a92b04b3d80c9ff918de424 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 20 Nov 2018 14:27:06 +0200 Subject: [PATCH 07/22] Allow toHash to infer safety --- src/core/experimental/array.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 52c4d56bf1..dc5a54810b 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -1850,8 +1850,7 @@ public: /// auto toHash() { - // will be safe with 2.082 - return () @trusted { return support.hashOf; }(); + return payload.hashOf; } /// From 86ea7ed0fe7e6dd416cc44d5d44960c40cb621dd Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 20 Nov 2018 14:53:57 +0200 Subject: [PATCH 08/22] Implement opEquals and remove global equals fn --- src/core/experimental/array.d | 183 +++++++++++++++++----------------- 1 file changed, 93 insertions(+), 90 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index dc5a54810b..882e0eb965 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -275,35 +275,6 @@ if (is(typeof(collection.popFront()))) return collection; } -auto equal(T, U)(T a, U b) -if (is(ElementType!T : ElementType!U) - && (is(T : V[], V) || is(T : Array!V2, V2)) - && (is(U : V4[], V4) || is(U : Array!V3, V3))) -{ - if (a.length != b.length) return false; - - while (a.length) - { - if (a[0] != b[0]) return false; - a = a[1 .. $]; - b = b[1 .. $]; - } - return true; -} - -@safe unittest -{ - auto a = [1, 2, 3]; - auto b = Array!int(a); - - assert(equal(a, a)); - assert(equal(a, b)); - assert(equal(b, a)); - assert(equal(b, b)); - a ~= 1; - assert(!equal(a, b)); -} - struct PrefixAllocator { /** @@ -640,18 +611,18 @@ public: // Create a list from a list of ints { auto a = Array!int(1, 2, 3); - assert(equal(a, [1, 2, 3])); + assert(a == [1, 2, 3]); } // Create a list from an array of ints { auto a = Array!int([1, 2, 3]); - assert(equal(a, [1, 2, 3])); + assert(a == [1, 2, 3]); } // Create a list from a list from an input range { auto a = Array!int(1, 2, 3); auto a2 = Array!int(a); - assert(equal(a2, [1, 2, 3])); + assert(a2 == [1, 2, 3]); } } @@ -955,7 +926,7 @@ public: Array!int a; a.reserve(stuff.length); a ~= stuff; - assert(equal(a, stuff)); + assert(a == stuff); } /** @@ -1030,7 +1001,7 @@ public: size_t pos = 0; pos += a.insert(pos, 1); pos += a.insert(pos, [2, 3]); - assert(equal(a, [1, 2, 3])); + assert(a == [1, 2, 3]); } /** @@ -1369,7 +1340,7 @@ public: auto stuff = [1, 2, 3]; auto a = immutable Array!int(stuff); auto aDup = a.dup; - assert(equal(aDup, stuff)); + assert(aDup == stuff); aDup.front = 0; assert(aDup.front == 0); assert(a.front == 1); @@ -1420,8 +1391,8 @@ public: { auto stuff = [1, 2, 3]; auto a = Array!int(stuff); - assert(equal(a[], stuff)); - assert(equal(a[1 .. $], stuff[1 .. $])); + assert(a[] == stuff); + assert(a[1 .. $] == stuff[1 .. $]); } /** @@ -1546,7 +1517,7 @@ public: { auto a = Array!int([1, 2, 3]); a[] = 0; - assert(a.equal([0, 0, 0])); + assert(a == [0, 0, 0]); } /** @@ -1580,7 +1551,7 @@ public: { auto a = Array!int([1, 2, 3, 4, 5, 6]); a[1 .. 3] = 0; - assert(a.equal([1, 0, 0, 4, 5, 6])); + assert(a == [1, 0, 0, 4, 5, 6]); } /** @@ -1663,9 +1634,9 @@ public: auto a = Array!int(1); auto a2 = a ~ 2; - assert(equal(a2, [1, 2])); + assert(a2 == [1, 2]); a.front = 0; - assert(equal(a2, [1, 2])); + assert(a2 == [1, 2]); } /** @@ -1711,9 +1682,9 @@ public: auto a2 = Array!int(1, 2); a = a2; // this will free the old a - assert(equal(a, [1, 2])); + assert(a == [1, 2]); a.front = 0; - assert(equal(a2, [0, 2])); + assert(a2 == [0, 2]); } static if (is(T == int)) @@ -1723,8 +1694,8 @@ public: auto arr1 = arr[1 .. $]; auto arr2 = arr[3 .. $]; arr1 = arr2; - assert(arr1.equal([4, 5, 6])); - assert(arr2.equal([4, 5, 6])); + assert(arr1 == [4, 5, 6]); + assert(arr2 == [4, 5, 6]); } /** @@ -1763,19 +1734,51 @@ public: a ~= 1; a ~= [2, 3]; - assert(equal(a, [1, 2, 3])); + assert(a == [1, 2, 3]); // append an input range a ~= a2; - assert(equal(a, [1, 2, 3, 4, 5])); + assert(a == [1, 2, 3, 4, 5]); a2.front = 0; - assert(equal(a, [1, 2, 3, 4, 5])); + assert(a == [1, 2, 3, 4, 5]); } /// - bool opEquals()(auto ref typeof(this) rhs) const + bool opEquals(U)(const U rhs) const + if (is(U : const typeof(this)) + || (is(U : const V[], V) && is(typeof(T.init == V.init)))) + { + auto a = this; + if (a.length != rhs.length) return false; + + for (size_t i = 0; i < a.length; ++i) + { + if (a[i] != rhs[i]) return false; + } + return true; + } + + static if (is(T == int)) + @safe unittest { - return payload.equal(rhs); + auto a = [1, 2, 3]; + auto b = Array!int(a); + + assert(a == a); + assert(a == b); + assert(b == a); + assert(b == b); + a ~= 1; + assert(a != b); + + static struct S + { + int x; + bool opEquals(int rhs) const { return x == rhs; } + } + + auto s = [S(1), S(2), S(3)]; + assert(b == s); } /// @@ -1871,30 +1874,30 @@ void testConcatAndAppend() Array!(int) a2 = Array!(int)(); auto a3 = a ~ a2; - assert(equal(a3, [1, 2, 3])); + assert(a3 == [1, 2, 3]); auto a4 = a3; a3 = a3 ~ 4; - assert(equal(a3, [1, 2, 3, 4])); + assert(a3 == [1, 2, 3, 4]); a3 = a3 ~ [5]; - assert(equal(a3, [1, 2, 3, 4, 5])); - assert(equal(a4, [1, 2, 3])); + assert(a3 == [1, 2, 3, 4, 5]); + assert(a4 == [1, 2, 3]); a4 = a3; a3 ~= 6; - assert(equal(a3, [1, 2, 3, 4, 5, 6])); + assert(a3 == [1, 2, 3, 4, 5, 6]); a3 ~= [7]; a3 ~= a3; - assert(equal(a3, [1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7])); + assert(a3 == [1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]); Array!int a5 = Array!(int)(); a5 ~= [1, 2, 3]; - assert(equal(a5, [1, 2, 3])); + assert(a5 == [1, 2, 3]); auto a6 = a5; a5 = a5; a5[0] = 10; - assert(equal(a5, a6)); + assert(a5 == a6); // Test concat with mixed qualifiers auto a7 = immutable Array!(int)(a5); @@ -1902,11 +1905,11 @@ void testConcatAndAppend() a5.front = 1; assert(a7.front == 10); auto a8 = a5 ~ a7; - assert(equal(a8, [1, 2, 3, 10, 2, 3])); + assert(a8 == [1, 2, 3, 10, 2, 3]); auto a9 = const Array!(int)(a5); auto a10 = a5 ~ a9; - assert(equal(a10, [1, 2, 3, 1, 2, 3])); + assert(a10 == [1, 2, 3, 1, 2, 3]); } @safe unittest @@ -1928,24 +1931,24 @@ void testSimple() size_t pos = 0; a.insert(pos, 1, 2, 3); assert(a.front == 1); - assert(equal(a, a)); - assert(equal(a, [1, 2, 3])); + assert(a == a); + assert(a == [1, 2, 3]); a.popFront(); assert(a.front == 2); - assert(equal(a, [2, 3])); + assert(a == [2, 3]); a.insert(pos, [4, 5, 6]); a.insert(pos, 7); a.insert(pos, [8]); - assert(equal(a, [8, 7, 4, 5, 6, 2, 3])); + assert(a == [8, 7, 4, 5, 6, 2, 3]); a.insert(a.length, 0, 1); a.insert(a.length, [-1, -2]); - assert(equal(a, [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); + assert(a == [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]); a.front = 9; - assert(equal(a, [9, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); + assert(a == [9, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]); auto aTail = a.tail; assert(aTail.front == 7); @@ -1973,22 +1976,22 @@ void testSimpleImmutable() size_t pos = 0; a.insert(pos, 1, 2, 3); assert(a.front == 1); - assert(equal(a, a)); - assert(equal(a, [1, 2, 3])); + assert(a == a); + assert(a == [1, 2, 3]); a.popFront(); assert(a.front == 2); - assert(equal(a, [2, 3])); + assert(a == [2, 3]); assert(a.tail.front == 3); a.insert(pos, [4, 5, 6]); a.insert(pos, 7); a.insert(pos, [8]); - assert(equal(a, [8, 7, 4, 5, 6, 2, 3])); + assert(a == [8, 7, 4, 5, 6, 2, 3]); a.insert(a.length, 0, 1); a.insert(a.length, [-1, -2]); - assert(equal(a, [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2])); + assert(a == [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]); // Cannot modify immutable values static assert(!__traits(compiles, a.front = 9)); @@ -2008,24 +2011,24 @@ void testCopyAndRef() { auto aFromList = Array!int(1, 2, 3); auto aFromRange = Array!int(aFromList); - assert(equal(aFromList, aFromRange)); + assert(aFromList == aFromRange); aFromList.popFront(); - assert(equal(aFromList, [2, 3])); - assert(equal(aFromRange, [1, 2, 3])); + assert(aFromList == [2, 3]); + assert(aFromRange == [1, 2, 3]); size_t pos = 0; Array!int aInsFromRange = Array!int(); aInsFromRange.insert(pos, aFromList); aFromList.popFront(); - assert(equal(aFromList, [3])); - assert(equal(aInsFromRange, [2, 3])); + assert(aFromList == [3]); + assert(aInsFromRange == [2, 3]); Array!int aInsBackFromRange = Array!int(); aInsBackFromRange.insert(pos, aFromList); aFromList.popFront(); assert(aFromList.empty); - assert(equal(aInsBackFromRange, [3])); + assert(aInsBackFromRange == [3]); auto aFromRef = aInsFromRange; auto aFromDup = aInsFromRange.dup; @@ -2062,12 +2065,12 @@ void testImmutability() // Create a mutable copy from an immutable array auto a5 = a.dup(); - assert(equal(a5, [1, 2, 3])); + assert(a5 == [1, 2, 3]); assert(a5.front == 1); a5.front = 2; assert(a5.front == 2); assert(a.front == 1); - assert(equal(a5, [2, 2, 3])); + assert(a5 == [2, 2, 3]); // Create immtable copies from mutable, const and immutable { @@ -2131,10 +2134,10 @@ void testWithStruct() auto arrayOfArrays = Array!(Array!int)(array); assert(array.opCmpPrefix!"=="(array.support, 2)); - assert(equal(arrayOfArrays.front, [1, 2, 3])); + assert(arrayOfArrays.front == [1, 2, 3]); arrayOfArrays.front.front = 2; - assert(equal(arrayOfArrays.front, [2, 2, 3])); - assert(equal(arrayOfArrays.front, array)); + assert(arrayOfArrays.front == [2, 2, 3]); + assert(arrayOfArrays.front == array); static assert(!__traits(compiles, arrayOfArrays.insert(1))); auto immArrayOfArrays = immutable Array!(Array!int)(array); @@ -2150,7 +2153,7 @@ void testWithStruct() static assert(!__traits(compiles, immArrayOfArrays.front = array)); } assert(array.opCmpPrefix!"=="(array.support, 1)); - assert(equal(array, [3, 2, 3])); + assert(array == [3, 2, 3]); } @safe unittest @@ -2239,22 +2242,22 @@ void testSlice() { auto a = Array!int(1, 2, 3, 4); auto b = a[]; - assert(equal(a, b)); + assert(a == b); b[1] = 5; assert(a[1] == 5); size_t startPos = 2; auto c = b[startPos .. $]; - assert(equal(c, [3, 4])); + assert(c == [3, 4]); c[0] = 5; - assert(equal(a, b)); - assert(equal(a, [1, 5, 5, 4])); + assert(a == b); + assert(a == [1, 5, 5, 4]); assert(a.capacity == b.capacity && b.capacity == c.capacity + startPos); c ~= 5; - assert(equal(c, [5, 4, 5])); - assert(equal(a, b)); - assert(equal(a, [1, 5, 5, 4])); + assert(c == [5, 4, 5]); + assert(a == b); + assert(a == [1, 5, 5, 4]); } @safe unittest From 025b50fd2a640ec9a8f4573732edafa484dc7f1c Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 20 Nov 2018 15:25:05 +0200 Subject: [PATCH 09/22] Make global symbols private --- src/core/experimental/array.d | 55 ++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 882e0eb965..07c987990a 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -12,7 +12,7 @@ Returns the size in bytes of the state that needs to be allocated to hold an object of type `T`. `stateSize!T` is zero for `struct`s that are not nested and have no nonstatic member variables. */ -template stateSize(T) +private template stateSize(T) { static if (is(T == class) || is(T == interface)) enum stateSize = __traits(classInstanceSize, T); @@ -22,13 +22,13 @@ template stateSize(T) enum stateSize = T.sizeof; } -template isAbstractClass(T...) +private template isAbstractClass(T...) if (T.length == 1) { enum bool isAbstractClass = __traits(isAbstractClass, T[0]); } -template isInnerClass(T) +private template isInnerClass(T) if (is(T == class)) { static if (is(typeof(T.outer))) @@ -37,9 +37,9 @@ if (is(T == class)) enum isInnerClass = false; } -enum classInstanceAlignment(T) = size_t.alignof >= T.alignof ? size_t.alignof : T.alignof; +private enum classInstanceAlignment(T) = size_t.alignof >= T.alignof ? size_t.alignof : T.alignof; -T emplace(T, Args...)(T chunk, auto ref Args args) +private T emplace(T, Args...)(T chunk, auto ref Args args) if (is(T == class)) { static assert(!isAbstractClass!T, T.stringof ~ @@ -77,7 +77,7 @@ if (is(T == class)) return chunk; } -T emplace(T, Args...)(void[] chunk, auto ref Args args) +private T emplace(T, Args...)(void[] chunk, auto ref Args args) if (is(T == class)) { enum classSize = __traits(classInstanceSize, T); @@ -85,7 +85,7 @@ if (is(T == class)) return emplace!T(cast(T)(chunk.ptr), args); } -T* emplace(T, Args...)(void[] chunk, auto ref Args args) +private T* emplace(T, Args...)(void[] chunk, auto ref Args args) if (!is(T == class)) { testEmplaceChunk(chunk, T.sizeof, T.alignof); @@ -93,20 +93,20 @@ if (!is(T == class)) return cast(T*) chunk.ptr; } -T* emplace(T)(T* chunk) @safe pure nothrow +private T* emplace(T)(T* chunk) @safe pure nothrow { emplaceRef!T(*chunk); return chunk; } -T* emplace(T, Args...)(T* chunk, auto ref Args args) +private T* emplace(T, Args...)(T* chunk, auto ref Args args) if (is(T == struct) || Args.length == 1) { emplaceRef!T(*chunk, args); return chunk; } -package void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) +private void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) { static if (args.length == 0) { @@ -172,7 +172,7 @@ package void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args) } } // ditto -package void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) +private void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) if (is(UT == Unqual!UT)) { emplaceRef!(UT, UT)(chunk, args); @@ -201,11 +201,10 @@ void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment) assert((cast(size_t) chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned."); } -enum hasElaborateDestructor(T) = __traits(compiles, { T t; t.__dtor(); }) - || __traits(compiles, { T t; t.__xdtor(); }); - -void dispose(A, T)(auto ref A alloc, auto ref T* p) +private void dispose(A, T)(auto ref A alloc, auto ref T* p) { + import core.internal.traits : hasElaborateDestructor; + static if (hasElaborateDestructor!T) { destroy(*p); @@ -215,7 +214,7 @@ void dispose(A, T)(auto ref A alloc, auto ref T* p) p = null; } -void dispose(A, T)(auto ref A alloc, auto ref T p) +private void dispose(A, T)(auto ref A alloc, auto ref T p) if (is(T == class) || is(T == interface)) { if (!p) return; @@ -238,8 +237,10 @@ if (is(T == class) || is(T == interface)) p = null; } -void dispose(A, T)(auto ref A alloc, auto ref T[] array) +private void dispose(A, T)(auto ref A alloc, auto ref T[] array) { + import core.internal.traits : hasElaborateDestructor; + static if (hasElaborateDestructor!(typeof(array[0]))) { foreach (ref e; array) @@ -260,7 +261,7 @@ void dispose(A, T)(auto ref A alloc, auto ref T[] array) The element type of `R`. `R` does not have to be a range. The element type is determined as the type yielded by `r[0]` for an object `r` of type `R`. */ -template ElementType(R) +private template ElementType(R) { static if (is(typeof(R.init[0].init) T)) alias ElementType = T; @@ -275,7 +276,7 @@ if (is(typeof(collection.popFront()))) return collection; } -struct PrefixAllocator +private struct PrefixAllocator { /** The alignment is a static constant equal to `platformAlignment`, which @@ -393,16 +394,16 @@ version(unittest) private alias SCAlloc = shared PrefixAllocator; private alias SSCAlloc = shared PrefixAllocator; - SCAlloc localAllocator; - SSCAlloc sharedAllocator; + private SCAlloc localAllocator; + private SSCAlloc sharedAllocator; - @nogc nothrow pure @trusted + private @nogc nothrow pure @trusted void[] pureAllocate(bool isShared, size_t n) { return (cast(void[] function(bool, size_t) @nogc nothrow pure)(&_allocate))(isShared, n); } - @nogc nothrow @safe + private @nogc nothrow @safe void[] _allocate(bool isShared, size_t n) { return isShared ? sharedAllocator.allocate(n) : localAllocator.allocate(n); @@ -410,26 +411,26 @@ version(unittest) static if (__traits(hasMember, typeof(localAllocator), "expand")) { - @nogc nothrow pure @trusted + private @nogc nothrow pure @trusted bool pureExpand(bool isShared, ref void[] b, size_t delta) { return (cast(bool function(bool, ref void[], size_t) @nogc nothrow pure)(&_expand))(isShared, b, delta); } - @nogc nothrow @safe + private @nogc nothrow @safe bool _expand(bool isShared, ref void[] b, size_t delta) { return isShared ? sharedAllocator.expand(b, delta) : localAllocator.expand(b, delta); } } - @nogc nothrow pure + private @nogc nothrow pure void pureDispose(T)(bool isShared, T[] b) { return (cast(void function(bool, T[]) @nogc nothrow pure)(&_dispose!(T)))(isShared, b); } - @nogc nothrow + private @nogc nothrow void _dispose(T)(bool isShared, T[] b) { return isShared ? sharedAllocator.dispose(b) : localAllocator.dispose(b); From 5fa4cbdbdc49e66d129a580aa77beb39346612b7 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 20 Nov 2018 16:48:24 +0200 Subject: [PATCH 10/22] Remove array's internal range API --- src/core/experimental/array.d | 313 +++++++++------------------------- 1 file changed, 80 insertions(+), 233 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 07c987990a..8cb2c792cf 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -269,13 +269,6 @@ private template ElementType(R) alias ElementType = void; } -auto tail(Collection)(Collection collection) -if (is(typeof(collection.popFront()))) -{ - collection.popFront(); - return collection; -} - private struct PrefixAllocator { /** @@ -491,7 +484,6 @@ private: size_t opPrefix(string op, T)(const T[] _support, size_t val) const if ((op == "+=") || (op == "-=")) { - return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(_support, val); } @@ -555,16 +547,16 @@ private: } assert(stuffLength == 0 || (stuffLength > 0 && tmpSupport !is null)); - size_t i = 0; - foreach (ref item; } ~ stuff ~ q{) - { - alias TT = ElementType!(typeof(payload)); - size_t s = i * stateSize!TT; - size_t e = (i + 1) * stateSize!TT; - void[] tmp = tmpSupport[s .. e]; - i++; - (() @trusted => emplace!TT(tmp, item))(); - } + for (size_t i = 0; i < stuffLength; ++i) + } ~ "" + ~"{" + ~" alias TT = ElementType!(typeof(payload));" + ~" size_t s = i * stateSize!TT;" + ~" size_t e = (i + 1) * stateSize!TT;" + ~" void[] tmp = tmpSupport[s .. e];" + ~" (() @trusted => emplace!TT(tmp, " ~ stuff ~ "[i]))();" + ~"}" + ~q{ support = (() @trusted => cast(typeof(support))(tmpSupport))(); payload = (() @trusted => cast(typeof(payload))(support[0 .. stuffLength]))(); @@ -752,7 +744,7 @@ public: } /** - * Return the number of elements in the array.. + * Return the number of elements in the array. * * Returns: * the length of the array. @@ -997,7 +989,7 @@ public: @safe unittest { Array!int a; - assert(a.empty); + assert(a.length == 0); size_t pos = 0; pos += a.insert(pos, 1); @@ -1034,124 +1026,12 @@ public: { auto a2 = a; assert(!a.isUnique); - a2.front = 0; - assert(a.front == 0); + a2[0] = 0; + assert(a[0] == 0); } // a2 goes out of scope assert(a.isUnique); } - /** - * Check if the array is empty. - * - * Returns: - * `true` if there are no elements in the array; `false` otherwise. - * - * Complexity: $(BIGOH 1). - */ - @nogc nothrow pure @safe - bool empty() const - { - return length == 0; - } - - /// - static if (is(T == int)) - @safe unittest - { - Array!int a; - assert(a.empty); - size_t pos = 0; - a.insert(pos, 1); - assert(!a.empty); - } - - /** - * Provide access to the first element in the array. The user must check - * that the array isn't `empty`, prior to calling this function. - * - * Returns: - * a reference to the first element. - * - * Complexity: $(BIGOH 1). - */ - ref auto front(this _)() - { - assert(!empty, "Array.front: Array is empty"); - return payload[0]; - } - - /// - static if (is(T == int)) - @safe unittest - { - auto a = Array!int(1, 2, 3); - assert(a.front == 1); - a.front = 0; - assert(a.front == 0); - } - - /** - * Advance to the next element in the array. The user must check - * that the array isn't `empty`, prior to calling this function. - * - * Complexity: $(BIGOH 1). - */ - @nogc nothrow pure @safe - void popFront() - { - assert(!empty, "Array.popFront: Array is empty"); - payload = payload[1 .. $]; - } - - /// - static if (is(T == int)) - @safe unittest - { - auto stuff = [1, 2, 3]; - auto a = Array!int(stuff); - size_t i = 0; - while (!a.empty) - { - assert(a.front == stuff[i++]); - a.popFront; - } - assert(a.empty); - } - - /** - * Advance to the next element in the array. The user must check - * that the array isn't `empty`, prior to calling this function. - * - * This must be used in order to iterate through a `const` or `immutable` - * array For a mutable array this is equivalent to calling `popFront`. - * - * Returns: - * an array that starts with the next element in the original array. - * - * Complexity: $(BIGOH 1). - */ - Qualified tail(this Qualified)() - { - assert(!empty, "Array.tail: Array is empty"); - - static if (is(Qualified == immutable) || is(Qualified == const)) - { - return this[1 .. $]; - } - else - { - return .tail(this); - } - } - - /// - static if (is(T == int)) - @safe unittest - { - auto ia = immutable Array!int([1, 2, 3]); - assert(ia.tail.front == 2); - } - /** * Eagerly iterate over each element in the array and call `fun` over each * element. This should be used to iterate through `const` and `immutable` @@ -1234,37 +1114,6 @@ public: //return 0; //} - /** - * Perform a shallow copy of the array. - * - * Returns: - * a new reference to the current array. - * - * Complexity: $(BIGOH 1). - */ - ref auto save(this _)() - { - return this; - } - - /// - static if (is(T == int)) - @safe unittest - { - auto stuff = [1, 2, 3]; - auto a = Array!int(stuff); - size_t i = 0; - - auto tmp = a.save; - while (!tmp.empty) - { - assert(tmp.front == stuff[i++]); - tmp.popFront; - } - assert(tmp.empty); - assert(!a.empty); - } - /** * Perform an immutable copy of the array. This will create a new array that * will copy the elements of the current array. This will `NOT` call `dup` on @@ -1342,14 +1191,14 @@ public: auto a = immutable Array!int(stuff); auto aDup = a.dup; assert(aDup == stuff); - aDup.front = 0; - assert(aDup.front == 0); - assert(a.front == 1); + aDup[0] = 0; + assert(aDup[0] == 0); + assert(a[0] == 1); } /** - * Return a slice to the current array. This is equivalent to calling - * `save`. + * Return a slice to the current array. This is equivalent to performing + * a shallow copy of the array. * * Returns: * an array that references the current array. @@ -1358,7 +1207,7 @@ public: */ Qualified opSlice(this Qualified)() { - return this.save; + return this; } /** @@ -1636,7 +1485,7 @@ public: auto a2 = a ~ 2; assert(a2 == [1, 2]); - a.front = 0; + a[0] = 0; assert(a2 == [1, 2]); } @@ -1684,7 +1533,7 @@ public: a = a2; // this will free the old a assert(a == [1, 2]); - a.front = 0; + a[0] = 0; assert(a2 == [0, 2]); } @@ -1731,7 +1580,7 @@ public: { Array!int a; auto a2 = Array!int(4, 5); - assert(a.empty); + assert(a.length == 0); a ~= 1; a ~= [2, 3]; @@ -1740,7 +1589,7 @@ public: // append an input range a ~= a2; assert(a == [1, 2, 3, 4, 5]); - a2.front = 0; + a2[0] = 0; assert(a == [1, 2, 3, 4, 5]); } @@ -1902,9 +1751,9 @@ void testConcatAndAppend() // Test concat with mixed qualifiers auto a7 = immutable Array!(int)(a5); - assert(a7.front == 10); - a5.front = 1; - assert(a7.front == 10); + assert(a7[0] == 10); + a5[0] = 1; + assert(a7[0] == 10); auto a8 = a5 ~ a7; assert(a8 == [1, 2, 3, 10, 2, 3]); @@ -1926,17 +1775,17 @@ version(unittest) private nothrow pure @safe void testSimple() { auto a = Array!int(); - assert(a.empty); + assert(a.length == 0); assert(a.isUnique); size_t pos = 0; a.insert(pos, 1, 2, 3); - assert(a.front == 1); + assert(a[0] == 1); assert(a == a); assert(a == [1, 2, 3]); - a.popFront(); - assert(a.front == 2); + a = a[1 .. $]; + assert(a[0] == 2); assert(a == [2, 3]); a.insert(pos, [4, 5, 6]); @@ -1948,14 +1797,14 @@ void testSimple() a.insert(a.length, [-1, -2]); assert(a == [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]); - a.front = 9; + a[0] = 9; assert(a == [9, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]); - auto aTail = a.tail; - assert(aTail.front == 7); - aTail.front = 8; - assert(aTail.front == 8); - assert(a.tail.front == 8); + auto aTail = a[1 .. $]; + assert(aTail[0] == 7); + aTail[0] = 8; + assert(aTail[0] == 8); + assert(a[1 .. $][0] == 8); assert(!a.isUnique); } @@ -1972,18 +1821,18 @@ version(unittest) private nothrow pure @safe void testSimpleImmutable() { auto a = Array!(immutable int)(); - assert(a.empty); + assert(a.length == 0); size_t pos = 0; a.insert(pos, 1, 2, 3); - assert(a.front == 1); + assert(a[0] == 1); assert(a == a); assert(a == [1, 2, 3]); - a.popFront(); - assert(a.front == 2); + a = a[1 .. $]; + assert(a[0] == 2); assert(a == [2, 3]); - assert(a.tail.front == 3); + assert(a[1 .. $][0] == 3); a.insert(pos, [4, 5, 6]); a.insert(pos, 7); @@ -1995,7 +1844,7 @@ void testSimpleImmutable() assert(a == [8, 7, 4, 5, 6, 2, 3, 0, 1, -1, -2]); // Cannot modify immutable values - static assert(!__traits(compiles, a.front = 9)); + static assert(!__traits(compiles, { a[0] = 9; })); } @safe unittest @@ -2014,29 +1863,29 @@ void testCopyAndRef() auto aFromRange = Array!int(aFromList); assert(aFromList == aFromRange); - aFromList.popFront(); + aFromList = aFromList[1 .. $]; assert(aFromList == [2, 3]); assert(aFromRange == [1, 2, 3]); size_t pos = 0; Array!int aInsFromRange = Array!int(); aInsFromRange.insert(pos, aFromList); - aFromList.popFront(); + aFromList = aFromList[1 .. $]; assert(aFromList == [3]); assert(aInsFromRange == [2, 3]); Array!int aInsBackFromRange = Array!int(); aInsBackFromRange.insert(pos, aFromList); - aFromList.popFront(); - assert(aFromList.empty); + aFromList = aFromList[1 .. $]; + assert(aFromList.length == 0); assert(aInsBackFromRange == [3]); auto aFromRef = aInsFromRange; auto aFromDup = aInsFromRange.dup; - assert(aInsFromRange.front == 2); - aFromRef.front = 5; - assert(aInsFromRange.front == 5); - assert(aFromDup.front == 2); + assert(aInsFromRange[0] == 2); + aFromRef[0] = 5; + assert(aInsFromRange[0] == 5); + assert(aFromDup[0] == 2); } @safe unittest @@ -2053,24 +1902,23 @@ void testImmutability() { auto a = immutable Array!(int)(1, 2, 3); auto a2 = a; - auto a3 = a2.save(); - assert(a2.front == 1); - assert(a2[0] == a2.front); - static assert(!__traits(compiles, a2.front = 4)); - static assert(!__traits(compiles, a2.popFront())); + assert(a2[0] == 1); + assert(a2[0] == a2[0]); + static assert(!__traits(compiles, { a2[0] = 4; })); + static assert(!__traits(compiles, { a2 = a2[1 .. $]; })); - auto a4 = a2.tail; - assert(a4.front == 2); + auto a4 = a2[1 .. $]; + assert(a4[0] == 2); static assert(!__traits(compiles, a4 = a4.tail)); // Create a mutable copy from an immutable array auto a5 = a.dup(); assert(a5 == [1, 2, 3]); - assert(a5.front == 1); - a5.front = 2; - assert(a5.front == 2); - assert(a.front == 1); + assert(a5[0] == 1); + a5[0] = 2; + assert(a5[0] == 2); + assert(a[0] == 1); assert(a5 == [2, 2, 3]); // Create immtable copies from mutable, const and immutable @@ -2101,18 +1949,17 @@ void testConstness() { auto a = const Array!(int)(1, 2, 3); auto a2 = a; - auto a3 = a2.save(); immutable Array!int a5 = a; assert(a5.opCmpPrefix!"=="(a5.support, 1)); - assert(a.opCmpPrefix!"=="(a.support, 3)); + assert(a.opCmpPrefix!"=="(a.support, 2)); - assert(a2.front == 1); - assert(a2[0] == a2.front); - static assert(!__traits(compiles, a2.front = 4)); - static assert(!__traits(compiles, a2.popFront())); + assert(a2[0] == 1); + assert(a2[0] == a2[0]); + static assert(!__traits(compiles, { a2[0] = 4; })); + static assert(!__traits(compiles, { a2 = a2[1 .. $]; })); - auto a4 = a2.tail; - assert(a4.front == 2); + auto a4 = a2[1 .. $]; + assert(a4[0] == 2); static assert(!__traits(compiles, a4 = a4.tail)); } @@ -2135,10 +1982,10 @@ void testWithStruct() auto arrayOfArrays = Array!(Array!int)(array); assert(array.opCmpPrefix!"=="(array.support, 2)); - assert(arrayOfArrays.front == [1, 2, 3]); - arrayOfArrays.front.front = 2; - assert(arrayOfArrays.front == [2, 2, 3]); - assert(arrayOfArrays.front == array); + assert(arrayOfArrays[0] == [1, 2, 3]); + arrayOfArrays[0][0] = 2; + assert(arrayOfArrays[0] == [2, 2, 3]); + assert(arrayOfArrays[0] == array); static assert(!__traits(compiles, arrayOfArrays.insert(1))); auto immArrayOfArrays = immutable Array!(Array!int)(array); @@ -2146,12 +1993,12 @@ void testWithStruct() // immutable is transitive, so it must iterate over array and // create a copy, and not set a ref assert(array.opCmpPrefix!"=="(array.support, 2)); - array.front = 3; - assert(immArrayOfArrays.front.front == 2); + array[0] = 3; + assert(immArrayOfArrays[0][0] == 2); assert(immArrayOfArrays.opCmpPrefix!"=="(immArrayOfArrays.support, 1)); - assert(immArrayOfArrays.front.opCmpPrefix!"=="(immArrayOfArrays.front.support, 1)); - static assert(!__traits(compiles, immArrayOfArrays.front.front = 2)); - static assert(!__traits(compiles, immArrayOfArrays.front = array)); + assert(immArrayOfArrays[0].opCmpPrefix!"=="(immArrayOfArrays[0].support, 1)); + static assert(!__traits(compiles, { immArrayOfArrays[0][0] = 2; })); + static assert(!__traits(compiles, { immArrayOfArrays[0] = array; })); } assert(array.opCmpPrefix!"=="(array.support, 1)); assert(array == [3, 2, 3]); @@ -2182,9 +2029,9 @@ void testWithClass() MyClass c = new MyClass(10); { Array!MyClass a = Array!MyClass(c); - assert(a.front.x == 10); - assert(a.front is c); - a.front.x = 20; + assert(a[0].x == 10); + assert(a[0] is c); + a[0].x = 20; } assert(c.x == 20); } From 65c0fde2160a9a8df56e3229677faba77e7c9f99 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 20 Nov 2018 21:05:08 +0000 Subject: [PATCH 11/22] Change from labels to inline access specifiers --- src/core/experimental/array.d | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 8cb2c792cf..afcb7df1f3 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -433,12 +433,10 @@ version(unittest) /// struct Array(T) { -private: - import core.atomic : atomicOp; - T[] payload; - Unqual!T[] support; + private T[] payload; + private Unqual!T[] support; version(unittest) { @@ -449,11 +447,11 @@ private: alias sharedAllocator = shared PrefixAllocator.instance; } - static enum double capacityFactor = 3.0 / 2; - static enum initCapacity = 3; - bool isShared; + private static enum double capacityFactor = 3.0 / 2; + private static enum initCapacity = 3; + private bool isShared; - @trusted + private @trusted auto pref() const { assert(support !is null); @@ -480,28 +478,28 @@ private: } } - @nogc nothrow pure @trusted + private @nogc nothrow pure @trusted size_t opPrefix(string op, T)(const T[] _support, size_t val) const if ((op == "+=") || (op == "-=")) { return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(_support, val); } - @nogc nothrow pure @trusted + private @nogc nothrow pure @trusted size_t opCmpPrefix(string op, T)(const T[] _support, size_t val) const if ((op == "==") || (op == "<=") || (op == "<") || (op == ">=") || (op == ">")) { return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(_support, val); } - @nogc nothrow pure @trusted + private @nogc nothrow pure @trusted void addRef(SupportQual, this Q)(SupportQual _support) { assert(_support !is null); cast(void) opPrefix!("+=")(_support, 1); } - void delRef(Unqual!T[] _support) + private void delRef(Unqual!T[] _support) { // Will be optimized away, but the type system infers T's safety if (0) { T t = T.init; } @@ -522,7 +520,7 @@ private: } } - static string immutableInsert(StuffType)(string stuff) + private static string immutableInsert(StuffType)(string stuff) { auto stuffLengthStr = q{ size_t stuffLength = } ~ stuff ~ ".length;"; @@ -564,7 +562,7 @@ private: }; } - void destroyUnused() + private void destroyUnused() { if (support !is null) { @@ -572,7 +570,6 @@ private: } } -public: /** * Constructs a qualified array out of a number of items * that will use the collection deciced allocator object. From 68191fab65eb48777ecc4ea56711905960114832 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 26 Nov 2018 16:17:44 +0200 Subject: [PATCH 12/22] Make `each` compatible with Phobos' `each` --- src/core/experimental/array.d | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index afcb7df1f3..24b775dbd9 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -1035,8 +1035,8 @@ struct Array(T) * arrays. * * Normally, the entire array is iterated. If partial iteration (early stopping) - * is desired, `fun` needs to return a value of type `int` (`-1` to stop, or - * anything else to continue the iteration. + * is desired, `fun` needs to return a value of a comparable type, `CT`, + * (`CT.init` to stop, or anything else to continue the iteration). * * Params: * fun = unary function to apply on each element of the array. @@ -1061,14 +1061,15 @@ struct Array(T) // The array is kept alive (rc > 0) from the caller scope foreach (ref e; this.payload) { - static if (!is(typeof(fn(T.init)) == int)) + alias Result = typeof(fn(e)); + static if (is(typeof(Result.init == Result.init))) { - cast(void) fn(e); + if (fn(e) == Result.init) + return false; } else { - if (fn(e) == -1) - return false; + cast(void) fn(e); } } return true; @@ -1082,7 +1083,7 @@ struct Array(T) auto ia = immutable Array!int([3, 2, 1]); static bool foo(int x) { return x > 0; } - static int bar(int x) { return x > 1 ? 1 : -1; } + static int bar(int x) { return x > 1 ? 1 : 0; } assert(ia.each!foo == true); assert(ia.each!bar == false); @@ -1094,7 +1095,7 @@ struct Array(T) auto ia = immutable Array!int([3, 2, 1]); static bool foo(int x) { return x > 0; } - static int bar(int x) { return x > 1 ? 1 : -1; } + static int bar(int x) { return x > 1 ? 1 : 0; } assert(ia.each!foo == true); assert(ia.each!bar == false); From 5d98a65e556123cf558fae2957ef3102a3d3cd9a Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 26 Nov 2018 16:26:41 +0200 Subject: [PATCH 13/22] DStyle: Add space between version ( --- src/core/experimental/array.d | 38 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 24b775dbd9..9722b9fa23 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -278,7 +278,7 @@ private struct PrefixAllocator enum uint alignment = size_t.alignof; static enum prefixSize = size_t.sizeof; - version(unittest) + version (unittest) { // During unittesting, we are keeping a count of the number of bytes allocated size_t bytesUsed; @@ -296,7 +296,7 @@ private struct PrefixAllocator // Init reference count to 0 *(cast(size_t *) p) = 0; - version(unittest) + version (unittest) { static if (is(typeof(this) == shared)) { @@ -318,7 +318,7 @@ private struct PrefixAllocator import core.memory : pureFree; assert(b !is null); - version(unittest) + version (unittest) { static if (is(typeof(this) == shared)) { @@ -382,7 +382,7 @@ private struct PrefixAllocator } -version(unittest) +version (unittest) { private alias SCAlloc = shared PrefixAllocator; private alias SSCAlloc = shared PrefixAllocator; @@ -438,7 +438,7 @@ struct Array(T) private T[] payload; private Unqual!T[] support; - version(unittest) + version (unittest) { } else @@ -508,7 +508,7 @@ struct Array(T) if (opPrefix!("-=")(_support, 1) == 0) { () @trusted { - version(unittest) + version (unittest) { pureDispose(isShared, _support); } @@ -527,7 +527,7 @@ struct Array(T) return stuffLengthStr ~ q{ - version(unittest) + version (unittest) { void[] tmpSupport = (() @trusted => pureAllocate(isShared, stuffLength * stateSize!T))(); } @@ -853,7 +853,7 @@ struct Array(T) if (support && opCmpPrefix!"=="(support, 1)) { void[] buf = support; - version(unittest) + version (unittest) { auto successfulExpand = pureExpand(isShared, buf, (n - capacity) * stateSize!T); } @@ -880,7 +880,7 @@ struct Array(T) } } - version(unittest) + version (unittest) { auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(isShared, n * stateSize!T)))(); } @@ -1715,7 +1715,7 @@ struct Array(T) } } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testConcatAndAppend() { auto a = Array!(int)(1, 2, 3); @@ -1769,7 +1769,7 @@ void testConcatAndAppend() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testSimple() { auto a = Array!int(); @@ -1815,7 +1815,7 @@ void testSimple() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testSimpleImmutable() { auto a = Array!(immutable int)(); @@ -1854,7 +1854,7 @@ void testSimpleImmutable() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testCopyAndRef() { auto aFromList = Array!int(1, 2, 3); @@ -1895,7 +1895,7 @@ void testCopyAndRef() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testImmutability() { auto a = immutable Array!(int)(1, 2, 3); @@ -1942,7 +1942,7 @@ void testImmutability() } } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testConstness() { auto a = const Array!(int)(1, 2, 3); @@ -1971,7 +1971,7 @@ void testConstness() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testWithStruct() { auto array = Array!int(1, 2, 3); @@ -2011,7 +2011,7 @@ void testWithStruct() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testWithClass() { class MyClass @@ -2043,7 +2043,7 @@ void testWithClass() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version(unittest) private @nogc nothrow pure @safe +version (unittest) private @nogc nothrow pure @safe void testOpOverloads() { auto a = Array!int(1, 2, 3, 4); @@ -2083,7 +2083,7 @@ void testOpOverloads() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version(unittest) private nothrow pure @safe +version (unittest) private nothrow pure @safe void testSlice() { auto a = Array!int(1, 2, 3, 4); From 4ac68126a0c92291ede1df37db58a21b302b758b Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 26 Nov 2018 17:15:46 +0200 Subject: [PATCH 14/22] Comply with D_NoBoundsCheck --- src/core/experimental/array.d | 58 ++++++++++++++++------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 9722b9fa23..712c7de553 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -1223,13 +1223,12 @@ struct Array(T) * Complexity: $(BIGOH 1) */ Qualified opSlice(this Qualified)(size_t start, size_t end) - in - { - assert(start <= end && end <= length, - "Array.opSlice(s, e): Invalid bounds: Ensure start <= end <= length"); - } - body { + version (D_NoBoundsChecks) { } else + { + assert(start <= end && end <= length, + "Array.opSlice(s, e): Invalid bounds: Ensure start <= end <= length"); + } return typeof(this)(support, payload[start .. end], isShared); } @@ -1256,12 +1255,11 @@ struct Array(T) * Complexity: $(BIGOH 1). */ ref auto opIndex(this _)(size_t idx) - in - { - assert(idx < length, "Array.opIndex: Index out of bounds"); - } - body { + version (D_NoBoundsChecks) { } else + { + assert(idx < length, "Array.opIndex: Index out of bounds"); + } return payload[idx]; } @@ -1286,12 +1284,11 @@ struct Array(T) * Complexity: $(BIGOH 1). */ ref auto opIndexUnary(string op)(size_t idx) - in - { - assert(idx < length, "Array.opIndexUnary!" ~ op ~ ": Index out of bounds"); - } - body { + version (D_NoBoundsChecks) { } else + { + assert(idx < length, "Array.opIndexUnary!" ~ op ~ ": Index out of bounds"); + } mixin("return " ~ op ~ "payload[idx];"); } @@ -1320,12 +1317,11 @@ struct Array(T) */ ref auto opIndexAssign(U)(U elem, size_t idx) if (is(U : T)) - in - { - assert(idx < length, "Array.opIndexAssign: Index out of bounds"); - } - body { + version (D_NoBoundsChecks) { } else + { + assert(idx < length, "Array.opIndexAssign: Index out of bounds"); + } return payload[idx] = elem; } @@ -1383,13 +1379,12 @@ struct Array(T) */ auto opSliceAssign(U)(U elem, size_t start, size_t end) if (is(U : T)) - in - { - assert(start <= end, "Array.opSliceAssign: Index out of bounds"); - assert(end < length, "Array.opSliceAssign: Index out of bounds"); - } - body { + version (D_NoBoundsChecks) { } else + { + assert(start <= end, "Array.opSliceAssign: Index out of bounds"); + assert(end < length, "Array.opSliceAssign: Index out of bounds"); + } return payload[start .. end] = elem; } @@ -1418,12 +1413,11 @@ struct Array(T) */ ref auto opIndexOpAssign(string op, U)(U elem, size_t idx) if (is(U : T)) - in - { - assert(idx < length, "Array.opIndexOpAssign!" ~ op ~ ": Index out of bounds"); - } - body { + version (D_NoBoundsChecks) { } else + { + assert(idx < length, "Array.opIndexOpAssign!" ~ op ~ ": Index out of bounds"); + } mixin("return payload[idx]" ~ op ~ "= elem;"); } From 97db99564484f8ae1fc0673627f5c80e0c093ddb Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Mon, 26 Nov 2018 17:24:30 +0200 Subject: [PATCH 15/22] Rename to `rcarray` --- src/core/experimental/array.d | 184 +++++++++++++++++----------------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 712c7de553..a9c9bd5698 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -431,7 +431,7 @@ version (unittest) } /// -struct Array(T) +struct rcarray(T) { import core.atomic : atomicOp; @@ -600,18 +600,18 @@ struct Array(T) { // Create a list from a list of ints { - auto a = Array!int(1, 2, 3); + auto a = rcarray!int(1, 2, 3); assert(a == [1, 2, 3]); } // Create a list from an array of ints { - auto a = Array!int([1, 2, 3]); + auto a = rcarray!int([1, 2, 3]); assert(a == [1, 2, 3]); } // Create a list from a list from an input range { - auto a = Array!int(1, 2, 3); - auto a2 = Array!int(a); + auto a = rcarray!int(1, 2, 3); + auto a2 = rcarray!int(a); assert(a2 == [1, 2, 3]); } } @@ -698,34 +698,34 @@ struct Array(T) static if (is(T == int)) @nogc nothrow pure @safe unittest { - auto a = Array!int(1, 2, 3); + auto a = rcarray!int(1, 2, 3); // Infer safety - static assert(!__traits(compiles, () @safe { Array!Unsafe(Unsafe(1)); })); - static assert(!__traits(compiles, () @safe { auto a = const Array!Unsafe(Unsafe(1)); })); - static assert(!__traits(compiles, () @safe { auto a = immutable Array!Unsafe(Unsafe(1)); })); + static assert(!__traits(compiles, () @safe { rcarray!Unsafe(Unsafe(1)); })); + static assert(!__traits(compiles, () @safe { auto a = const rcarray!Unsafe(Unsafe(1)); })); + static assert(!__traits(compiles, () @safe { auto a = immutable rcarray!Unsafe(Unsafe(1)); })); - static assert(!__traits(compiles, () @safe { Array!UnsafeDtor(UnsafeDtor(1)); })); - static assert(!__traits(compiles, () @safe { auto s = const Array!UnsafeDtor(UnsafeDtor(1)); })); - static assert(!__traits(compiles, () @safe { auto s = immutable Array!UnsafeDtor(UnsafeDtor(1)); })); + static assert(!__traits(compiles, () @safe { rcarray!UnsafeDtor(UnsafeDtor(1)); })); + static assert(!__traits(compiles, () @safe { auto s = const rcarray!UnsafeDtor(UnsafeDtor(1)); })); + static assert(!__traits(compiles, () @safe { auto s = immutable rcarray!UnsafeDtor(UnsafeDtor(1)); })); // Infer purity - static assert(!__traits(compiles, () pure { Array!Impure(Impure(1)); })); - static assert(!__traits(compiles, () pure { auto a = const Array!Impure(Impure(1)); })); - static assert(!__traits(compiles, () pure { auto a = immutable Array!Impure(Impure(1)); })); + static assert(!__traits(compiles, () pure { rcarray!Impure(Impure(1)); })); + static assert(!__traits(compiles, () pure { auto a = const rcarray!Impure(Impure(1)); })); + static assert(!__traits(compiles, () pure { auto a = immutable rcarray!Impure(Impure(1)); })); - static assert(!__traits(compiles, () pure { Array!ImpureDtor(ImpureDtor(1)); })); - static assert(!__traits(compiles, () pure { auto s = const Array!ImpureDtor(ImpureDtor(1)); })); - static assert(!__traits(compiles, () pure { auto s = immutable Array!ImpureDtor(ImpureDtor(1)); })); + static assert(!__traits(compiles, () pure { rcarray!ImpureDtor(ImpureDtor(1)); })); + static assert(!__traits(compiles, () pure { auto s = const rcarray!ImpureDtor(ImpureDtor(1)); })); + static assert(!__traits(compiles, () pure { auto s = immutable rcarray!ImpureDtor(ImpureDtor(1)); })); // Infer throwability - static assert(!__traits(compiles, () nothrow { Array!Throws(Throws(1)); })); - static assert(!__traits(compiles, () nothrow { auto a = const Array!Throws(Throws(1)); })); - static assert(!__traits(compiles, () nothrow { auto a = immutable Array!Throws(Throws(1)); })); + static assert(!__traits(compiles, () nothrow { rcarray!Throws(Throws(1)); })); + static assert(!__traits(compiles, () nothrow { auto a = const rcarray!Throws(Throws(1)); })); + static assert(!__traits(compiles, () nothrow { auto a = immutable rcarray!Throws(Throws(1)); })); - static assert(!__traits(compiles, () nothrow { Array!ThrowsDtor(ThrowsDtor(1)); })); - static assert(!__traits(compiles, () nothrow { auto s = const Array!ThrowsDtor(ThrowsDtor(1)); })); - static assert(!__traits(compiles, () nothrow { auto s = immutable Array!ThrowsDtor(ThrowsDtor(1)); })); + static assert(!__traits(compiles, () nothrow { rcarray!ThrowsDtor(ThrowsDtor(1)); })); + static assert(!__traits(compiles, () nothrow { auto s = const rcarray!ThrowsDtor(ThrowsDtor(1)); })); + static assert(!__traits(compiles, () nothrow { auto s = immutable rcarray!ThrowsDtor(ThrowsDtor(1)); })); } private @nogc nothrow pure @trusted @@ -761,7 +761,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int(1, 2, 3); + auto a = rcarray!int(1, 2, 3); assert(a.length == 3); assert(a[$ - 1] == 3); } @@ -791,7 +791,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int(1, 2, 3); + auto a = rcarray!int(1, 2, 3); a.length = 2; assert(a.length == 2); @@ -823,7 +823,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int(1, 2, 3); + auto a = rcarray!int(1, 2, 3); a.reserve(10); assert(a.capacity == 10); } @@ -913,7 +913,7 @@ struct Array(T) @safe unittest { auto stuff = [1, 2, 3]; - Array!int a; + rcarray!int a; a.reserve(stuff.length); a ~= stuff; assert(a == stuff); @@ -935,7 +935,7 @@ struct Array(T) * elements in the range. */ size_t insert(Stuff)(size_t pos, Stuff stuff) - if (is(Stuff == Array!T)) + if (is(Stuff == rcarray!T)) { mixin(insertImpl); } @@ -985,7 +985,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - Array!int a; + rcarray!int a; assert(a.length == 0); size_t pos = 0; @@ -1017,7 +1017,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int(24, 42); + auto a = rcarray!int(24, 42); assert(a.isUnique); { @@ -1080,7 +1080,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto ia = immutable Array!int([3, 2, 1]); + auto ia = immutable rcarray!int([3, 2, 1]); static bool foo(int x) { return x > 0; } static int bar(int x) { return x > 1 ? 1 : 0; } @@ -1092,7 +1092,7 @@ struct Array(T) @safe unittest { { - auto ia = immutable Array!int([3, 2, 1]); + auto ia = immutable rcarray!int([3, 2, 1]); static bool foo(int x) { return x > 0; } static int bar(int x) { return x > 1 ? 1 : 0; } @@ -1123,9 +1123,9 @@ struct Array(T) * * Complexity: $(BIGOH n). */ - immutable(Array!T) idup(this Q)() + immutable(rcarray!T) idup(this Q)() { - auto r = immutable Array!T(this); + auto r = immutable rcarray!T(this); return r; } @@ -1134,19 +1134,19 @@ struct Array(T) @safe unittest { { - auto a = Array!(int)(1, 2, 3); + auto a = rcarray!(int)(1, 2, 3); auto a2 = a.idup(); static assert (is(typeof(a2) == immutable)); } { - auto a = const Array!(int)(1, 2, 3); + auto a = const rcarray!(int)(1, 2, 3); auto a2 = a.idup(); static assert (is(typeof(a2) == immutable)); } { - auto a = immutable Array!(int)(1, 2, 3); + auto a = immutable rcarray!(int)(1, 2, 3); auto a2 = a.idup(); static assert (is(typeof(a2) == immutable)); } @@ -1162,9 +1162,9 @@ struct Array(T) * * Complexity: $(BIGOH n). */ - Array!T dup(this Q)() + rcarray!T dup(this Q)() { - Array!T result; + rcarray!T result; static if (is(Q == immutable) || is(Q == const)) { @@ -1186,7 +1186,7 @@ struct Array(T) @safe unittest { auto stuff = [1, 2, 3]; - auto a = immutable Array!int(stuff); + auto a = immutable rcarray!int(stuff); auto aDup = a.dup; assert(aDup == stuff); aDup[0] = 0; @@ -1237,7 +1237,7 @@ struct Array(T) @safe unittest { auto stuff = [1, 2, 3]; - auto a = Array!int(stuff); + auto a = rcarray!int(stuff); assert(a[] == stuff); assert(a[1 .. $] == stuff[1 .. $]); } @@ -1267,7 +1267,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int([1, 2, 3]); + auto a = rcarray!int([1, 2, 3]); assert(a[2] == 3); } @@ -1296,7 +1296,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int([1, 2, 3]); + auto a = rcarray!int([1, 2, 3]); int x = --a[2]; assert(a[2] == 2); assert(x == 2); @@ -1329,7 +1329,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int([1, 2, 3]); + auto a = rcarray!int([1, 2, 3]); a[2] = 2; assert(a[2] == 2); (a[2] = 3)++; @@ -1359,7 +1359,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int([1, 2, 3]); + auto a = rcarray!int([1, 2, 3]); a[] = 0; assert(a == [0, 0, 0]); } @@ -1392,7 +1392,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int([1, 2, 3, 4, 5, 6]); + auto a = rcarray!int([1, 2, 3, 4, 5, 6]); a[1 .. 3] = 0; assert(a == [1, 0, 0, 4, 5, 6]); } @@ -1425,7 +1425,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int([1, 2, 3]); + auto a = rcarray!int([1, 2, 3]); a[2] += 2; assert(a[2] == 5); (a[2] += 3)++; @@ -1473,7 +1473,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int(1); + auto a = rcarray!int(1); auto a2 = a ~ 2; assert(a2 == [1, 2]); @@ -1520,8 +1520,8 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto a = Array!int(1); - auto a2 = Array!int(1, 2); + auto a = rcarray!int(1); + auto a2 = rcarray!int(1, 2); a = a2; // this will free the old a assert(a == [1, 2]); @@ -1532,7 +1532,7 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto arr = Array!int(1, 2, 3, 4, 5, 6); + auto arr = rcarray!int(1, 2, 3, 4, 5, 6); auto arr1 = arr[1 .. $]; auto arr2 = arr[3 .. $]; arr1 = arr2; @@ -1570,8 +1570,8 @@ struct Array(T) static if (is(T == int)) @safe unittest { - Array!int a; - auto a2 = Array!int(4, 5); + rcarray!int a; + auto a2 = rcarray!int(4, 5); assert(a.length == 0); a ~= 1; @@ -1604,7 +1604,7 @@ struct Array(T) @safe unittest { auto a = [1, 2, 3]; - auto b = Array!int(a); + auto b = rcarray!int(a); assert(a == a); assert(a == b); @@ -1627,9 +1627,9 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto arr1 = Array!int(1, 2); - auto arr2 = Array!int(1, 2); - auto arr3 = Array!int(2, 3); + auto arr1 = rcarray!int(1, 2); + auto arr2 = rcarray!int(1, 2); + auto arr3 = rcarray!int(2, 3); assert(arr1 == arr2); assert(arr2 == arr1); assert(arr1 != arr3); @@ -1640,7 +1640,7 @@ struct Array(T) /// int opCmp(U)(auto ref U rhs) - if ((is(U == Array!V, V) || is(U == V[], V)) && is(V : T)) + if ((is(U == rcarray!V, V) || is(U == V[], V)) && is(V : T)) { auto r1 = this; auto r2 = rhs; @@ -1663,10 +1663,10 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto arr1 = Array!int(1, 2); - auto arr2 = Array!int(1, 2); - auto arr3 = Array!int(2, 3); - auto arr4 = Array!int(0, 3); + auto arr1 = rcarray!int(1, 2); + auto arr2 = rcarray!int(1, 2); + auto arr3 = rcarray!int(2, 3); + auto arr4 = rcarray!int(0, 3); assert(arr1 <= arr2); assert(arr2 >= arr1); assert(arr1 < arr3); @@ -1679,9 +1679,9 @@ struct Array(T) static if (is(T == int)) @safe unittest { - auto arr1 = Array!int(1, 2); + auto arr1 = rcarray!int(1, 2); auto arr2 = [1, 2]; - auto arr3 = Array!int(2, 3); + auto arr3 = rcarray!int(2, 3); auto arr4 = [0, 3]; assert(arr1 <= arr2); assert(arr2 >= arr1); @@ -1701,19 +1701,19 @@ struct Array(T) /// @safe unittest { - auto arr1 = Array!int(1, 2); - assert(arr1.toHash == Array!int(1, 2).toHash); + auto arr1 = rcarray!int(1, 2); + assert(arr1.toHash == rcarray!int(1, 2).toHash); arr1 ~= 3; - assert(arr1.toHash == Array!int(1, 2, 3).toHash); - assert(Array!int().toHash == Array!int().toHash); + assert(arr1.toHash == rcarray!int(1, 2, 3).toHash); + assert(rcarray!int().toHash == rcarray!int().toHash); } } version (unittest) private nothrow pure @safe void testConcatAndAppend() { - auto a = Array!(int)(1, 2, 3); - Array!(int) a2 = Array!(int)(); + auto a = rcarray!(int)(1, 2, 3); + rcarray!(int) a2 = rcarray!(int)(); auto a3 = a ~ a2; assert(a3 == [1, 2, 3]); @@ -1733,7 +1733,7 @@ void testConcatAndAppend() a3 ~= a3; assert(a3 == [1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]); - Array!int a5 = Array!(int)(); + rcarray!int a5 = rcarray!(int)(); a5 ~= [1, 2, 3]; assert(a5 == [1, 2, 3]); auto a6 = a5; @@ -1742,14 +1742,14 @@ void testConcatAndAppend() assert(a5 == a6); // Test concat with mixed qualifiers - auto a7 = immutable Array!(int)(a5); + auto a7 = immutable rcarray!(int)(a5); assert(a7[0] == 10); a5[0] = 1; assert(a7[0] == 10); auto a8 = a5 ~ a7; assert(a8 == [1, 2, 3, 10, 2, 3]); - auto a9 = const Array!(int)(a5); + auto a9 = const rcarray!(int)(a5); auto a10 = a5 ~ a9; assert(a10 == [1, 2, 3, 1, 2, 3]); } @@ -1766,7 +1766,7 @@ void testConcatAndAppend() version (unittest) private nothrow pure @safe void testSimple() { - auto a = Array!int(); + auto a = rcarray!int(); assert(a.length == 0); assert(a.isUnique); @@ -1812,7 +1812,7 @@ void testSimple() version (unittest) private nothrow pure @safe void testSimpleImmutable() { - auto a = Array!(immutable int)(); + auto a = rcarray!(immutable int)(); assert(a.length == 0); size_t pos = 0; @@ -1851,8 +1851,8 @@ void testSimpleImmutable() version (unittest) private nothrow pure @safe void testCopyAndRef() { - auto aFromList = Array!int(1, 2, 3); - auto aFromRange = Array!int(aFromList); + auto aFromList = rcarray!int(1, 2, 3); + auto aFromRange = rcarray!int(aFromList); assert(aFromList == aFromRange); aFromList = aFromList[1 .. $]; @@ -1860,13 +1860,13 @@ void testCopyAndRef() assert(aFromRange == [1, 2, 3]); size_t pos = 0; - Array!int aInsFromRange = Array!int(); + rcarray!int aInsFromRange = rcarray!int(); aInsFromRange.insert(pos, aFromList); aFromList = aFromList[1 .. $]; assert(aFromList == [3]); assert(aInsFromRange == [2, 3]); - Array!int aInsBackFromRange = Array!int(); + rcarray!int aInsBackFromRange = rcarray!int(); aInsBackFromRange.insert(pos, aFromList); aFromList = aFromList[1 .. $]; assert(aFromList.length == 0); @@ -1892,7 +1892,7 @@ void testCopyAndRef() version (unittest) private nothrow pure @safe void testImmutability() { - auto a = immutable Array!(int)(1, 2, 3); + auto a = immutable rcarray!(int)(1, 2, 3); auto a2 = a; assert(a2[0] == 1); @@ -1915,21 +1915,21 @@ void testImmutability() // Create immtable copies from mutable, const and immutable { - auto aa = Array!(int)(1, 2, 3); + auto aa = rcarray!(int)(1, 2, 3); auto aa2 = aa.idup(); assert(aa.opCmpPrefix!"=="(aa.support, 1)); assert(aa2.opCmpPrefix!"=="(aa2.support, 1)); } { - auto aa = const Array!(int)(1, 2, 3); + auto aa = const rcarray!(int)(1, 2, 3); auto aa2 = aa.idup(); assert(aa.opCmpPrefix!"=="(aa.support, 1)); assert(aa2.opCmpPrefix!"=="(aa2.support, 1)); } { - auto aa = immutable Array!(int)(1, 2, 3); + auto aa = immutable rcarray!(int)(1, 2, 3); auto aa2 = aa.idup(); assert(aa.opCmpPrefix!"=="(aa.support, 2)); assert(aa2.opCmpPrefix!"=="(aa2.support, 2)); @@ -1939,9 +1939,9 @@ void testImmutability() version (unittest) private nothrow pure @safe void testConstness() { - auto a = const Array!(int)(1, 2, 3); + auto a = const rcarray!(int)(1, 2, 3); auto a2 = a; - immutable Array!int a5 = a; + immutable rcarray!int a5 = a; assert(a5.opCmpPrefix!"=="(a5.support, 1)); assert(a.opCmpPrefix!"=="(a.support, 2)); @@ -1968,11 +1968,11 @@ void testConstness() version (unittest) private nothrow pure @safe void testWithStruct() { - auto array = Array!int(1, 2, 3); + auto array = rcarray!int(1, 2, 3); { assert(array.opCmpPrefix!"=="(array.support, 1)); - auto arrayOfArrays = Array!(Array!int)(array); + auto arrayOfArrays = rcarray!(rcarray!int)(array); assert(array.opCmpPrefix!"=="(array.support, 2)); assert(arrayOfArrays[0] == [1, 2, 3]); arrayOfArrays[0][0] = 2; @@ -1980,7 +1980,7 @@ void testWithStruct() assert(arrayOfArrays[0] == array); static assert(!__traits(compiles, arrayOfArrays.insert(1))); - auto immArrayOfArrays = immutable Array!(Array!int)(array); + auto immArrayOfArrays = immutable rcarray!(rcarray!int)(array); // immutable is transitive, so it must iterate over array and // create a copy, and not set a ref @@ -2020,7 +2020,7 @@ void testWithClass() MyClass c = new MyClass(10); { - Array!MyClass a = Array!MyClass(c); + rcarray!MyClass a = rcarray!MyClass(c); assert(a[0].x == 10); assert(a[0] is c); a[0].x = 20; @@ -2040,7 +2040,7 @@ void testWithClass() version (unittest) private @nogc nothrow pure @safe void testOpOverloads() { - auto a = Array!int(1, 2, 3, 4); + auto a = rcarray!int(1, 2, 3, 4); assert(a[0] == 1); // opIndex // opIndexUnary @@ -2080,7 +2080,7 @@ void testOpOverloads() version (unittest) private nothrow pure @safe void testSlice() { - auto a = Array!int(1, 2, 3, 4); + auto a = rcarray!int(1, 2, 3, 4); auto b = a[]; assert(a == b); b[1] = 5; From a144462506ddb84686d10d298cac549a0b4c891d Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 27 Nov 2018 13:50:26 +0200 Subject: [PATCH 16/22] Refactor dup --- src/core/experimental/array.d | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index a9c9bd5698..d1a9460918 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -1164,21 +1164,7 @@ struct rcarray(T) */ rcarray!T dup(this Q)() { - rcarray!T result; - - static if (is(Q == immutable) || is(Q == const)) - { - result.reserve(length); - foreach(i; 0 .. length) - { - result ~= this[i]; - } - } - else - { - result.insert(0, this); - } - return result; + return rcarray!T(payload); } /// From d0a8ae57036c5098b79a18cbb2ba74779b2c0921 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 27 Nov 2018 13:52:22 +0200 Subject: [PATCH 17/22] Refactor idup --- src/core/experimental/array.d | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index d1a9460918..5d4af5ac5e 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -1125,8 +1125,7 @@ struct rcarray(T) */ immutable(rcarray!T) idup(this Q)() { - auto r = immutable rcarray!T(this); - return r; + return immutable rcarray!T(this); } /// From 44109799fa8c551ffffa2d2cea962ac44d9aac71 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Thu, 29 Nov 2018 14:14:54 +0200 Subject: [PATCH 18/22] Use version (CoreUnittest) for stats allocator --- posix.mak | 7 ++++-- src/core/experimental/array.d | 46 +++++++++++++++++++---------------- win32.mak | 5 +++- win64.mak | 12 ++++++--- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/posix.mak b/posix.mak index 99d57b2999..1a3e971933 100644 --- a/posix.mak +++ b/posix.mak @@ -86,6 +86,9 @@ else DFLAGS:=$(UDFLAGS) -inline # unittests don't compile with -inline endif +# Unittest flags +UTFLAGS=-version=CoreUnittest -unittest + # Set PHOBOS_DFLAGS (for linking against Phobos) PHOBOS_PATH=../phobos SHARED=$(if $(findstring $(OS),linux freebsd),1,) @@ -289,7 +292,7 @@ $(addprefix $(ROOT)/unittest/,$(DISABLED_TESTS)) : ifeq (,$(SHARED)) $(ROOT)/unittest/test_runner: $(OBJS) $(SRCS) src/test_runner.d $(DMD) - $(DMD) $(UDFLAGS) -unittest -of$@ src/test_runner.d $(SRCS) $(OBJS) -debuglib= -defaultlib= + $(DMD) $(UDFLAGS) $(UTFLAGS) -of$@ src/test_runner.d $(SRCS) $(OBJS) -debuglib= -defaultlib= else @@ -297,7 +300,7 @@ UT_DRUNTIME:=$(ROOT)/unittest/libdruntime-ut$(DOTDLL) $(UT_DRUNTIME): UDFLAGS+=-version=Shared -fPIC $(UT_DRUNTIME): $(OBJS) $(SRCS) $(DMD) - $(DMD) $(UDFLAGS) -shared -unittest -of$@ $(SRCS) $(OBJS) $(LINKDL) -debuglib= -defaultlib= + $(DMD) $(UDFLAGS) -shared $(UTFLAGS) -of$@ $(SRCS) $(OBJS) $(LINKDL) -debuglib= -defaultlib= $(ROOT)/unittest/test_runner: $(UT_DRUNTIME) src/test_runner.d $(DMD) $(DMD) $(UDFLAGS) -of$@ src/test_runner.d -L$(UT_DRUNTIME) -debuglib= -defaultlib= diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 5d4af5ac5e..cdce40cdec 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -278,7 +278,7 @@ private struct PrefixAllocator enum uint alignment = size_t.alignof; static enum prefixSize = size_t.sizeof; - version (unittest) + version (CoreUnittest) { // During unittesting, we are keeping a count of the number of bytes allocated size_t bytesUsed; @@ -296,7 +296,7 @@ private struct PrefixAllocator // Init reference count to 0 *(cast(size_t *) p) = 0; - version (unittest) + version (CoreUnittest) { static if (is(typeof(this) == shared)) { @@ -318,7 +318,7 @@ private struct PrefixAllocator import core.memory : pureFree; assert(b !is null); - version (unittest) + version (CoreUnittest) { static if (is(typeof(this) == shared)) { @@ -366,6 +366,7 @@ private struct PrefixAllocator static shared PrefixAllocator instance; } +version (CoreUnittest) @safe pure nothrow @nogc unittest { shared PrefixAllocator a; @@ -382,7 +383,7 @@ private struct PrefixAllocator } -version (unittest) +version (CoreUnittest) { private alias SCAlloc = shared PrefixAllocator; private alias SSCAlloc = shared PrefixAllocator; @@ -438,9 +439,7 @@ struct rcarray(T) private T[] payload; private Unqual!T[] support; - version (unittest) - { - } + version (CoreUnittest) { } else { alias localAllocator = shared PrefixAllocator.instance; @@ -508,7 +507,7 @@ struct rcarray(T) if (opPrefix!("-=")(_support, 1) == 0) { () @trusted { - version (unittest) + version (CoreUnittest) { pureDispose(isShared, _support); } @@ -527,7 +526,7 @@ struct rcarray(T) return stuffLengthStr ~ q{ - version (unittest) + version (CoreUnittest) { void[] tmpSupport = (() @trusted => pureAllocate(isShared, stuffLength * stateSize!T))(); } @@ -853,7 +852,7 @@ struct rcarray(T) if (support && opCmpPrefix!"=="(support, 1)) { void[] buf = support; - version (unittest) + version (CoreUnittest) { auto successfulExpand = pureExpand(isShared, buf, (n - capacity) * stateSize!T); } @@ -880,7 +879,7 @@ struct rcarray(T) } } - version (unittest) + version (CoreUnittest) { auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(isShared, n * stateSize!T)))(); } @@ -1694,7 +1693,10 @@ struct rcarray(T) } } -version (unittest) private nothrow pure @safe +version (CoreUnittest) +{ // Begin CoreUnittest - conditional compilation so we can check for mem leaks + +private nothrow pure @safe void testConcatAndAppend() { auto a = rcarray!(int)(1, 2, 3); @@ -1748,7 +1750,7 @@ void testConcatAndAppend() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version (unittest) private nothrow pure @safe +private nothrow pure @safe void testSimple() { auto a = rcarray!int(); @@ -1794,7 +1796,7 @@ void testSimple() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version (unittest) private nothrow pure @safe +private nothrow pure @safe void testSimpleImmutable() { auto a = rcarray!(immutable int)(); @@ -1833,7 +1835,7 @@ void testSimpleImmutable() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version (unittest) private nothrow pure @safe +private nothrow pure @safe void testCopyAndRef() { auto aFromList = rcarray!int(1, 2, 3); @@ -1874,7 +1876,7 @@ void testCopyAndRef() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version (unittest) private nothrow pure @safe +private nothrow pure @safe void testImmutability() { auto a = immutable rcarray!(int)(1, 2, 3); @@ -1921,7 +1923,7 @@ void testImmutability() } } -version (unittest) private nothrow pure @safe +private nothrow pure @safe void testConstness() { auto a = const rcarray!(int)(1, 2, 3); @@ -1950,7 +1952,7 @@ void testConstness() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version (unittest) private nothrow pure @safe +private nothrow pure @safe void testWithStruct() { auto array = rcarray!int(1, 2, 3); @@ -1990,7 +1992,7 @@ void testWithStruct() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version (unittest) private nothrow pure @safe +private nothrow pure @safe void testWithClass() { class MyClass @@ -2022,7 +2024,7 @@ void testWithClass() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version (unittest) private @nogc nothrow pure @safe +private @nogc nothrow pure @safe void testOpOverloads() { auto a = rcarray!int(1, 2, 3, 4); @@ -2062,7 +2064,7 @@ void testOpOverloads() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } -version (unittest) private nothrow pure @safe +private nothrow pure @safe void testSlice() { auto a = rcarray!int(1, 2, 3, 4); @@ -2093,3 +2095,5 @@ void testSlice() assert(localAllocator.bytesUsed == 0, "Array ref count leaks memory"); assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } + +} // End CoreUnittest diff --git a/win32.mak b/win32.mak index 9052ac5858..5efe5b469f 100644 --- a/win32.mak +++ b/win32.mak @@ -17,6 +17,9 @@ DFLAGS=-m$(MODEL) -conf= -O -release -dip1000 -preview=fieldwise -inline -w -Isr UDFLAGS=-m$(MODEL) -conf= -O -release -dip1000 -preview=fieldwise -w -Isrc -Iimport DDOCFLAGS=-conf= -c -w -o- -Isrc -Iimport -version=CoreDdoc +# Unittest flags +UTFLAGS=-version=CoreUnittest -unittest + CFLAGS= DRUNTIME_BASE=druntime @@ -106,7 +109,7 @@ $(DRUNTIME): $(OBJS) $(SRCS) win$(MODEL).mak *$(DMD) -lib -of$(DRUNTIME) -Xfdruntime.json $(DFLAGS) $(SRCS) $(OBJS) unittest : $(SRCS) $(DRUNTIME) - *$(DMD) $(UDFLAGS) -L/co -unittest -ofunittest.exe -main $(SRCS) $(DRUNTIME) -debuglib=$(DRUNTIME) -defaultlib=$(DRUNTIME) + *$(DMD) $(UDFLAGS) -L/co $(UTFLAGS) -ofunittest.exe -main $(SRCS) $(DRUNTIME) -debuglib=$(DRUNTIME) -defaultlib=$(DRUNTIME) unittest ################### tests ###################################### diff --git a/win64.mak b/win64.mak index e408774c5b..5db7652d6b 100644 --- a/win64.mak +++ b/win64.mak @@ -20,10 +20,15 @@ IMPDIR=import MAKE=make -DFLAGS=-m$(MODEL) -conf= -O -release -dip1000 -preview=fieldwise -inline -w -Isrc -Iimport -UDFLAGS=-m$(MODEL) -conf= -O -release -dip1000 -preview=fieldwise -w -Isrc -Iimport +#DFLAGS=-m$(MODEL) -conf= -O -release -dip1000 -preview=fieldwise -inline -w -Isrc -Iimport +#UDFLAGS=-m$(MODEL) -conf= -O -release -dip1000 -preview=fieldwise -w -Isrc -Iimport +DFLAGS=-m$(MODEL) -conf= -O -release -dip1000 -inline -w -Isrc -Iimport +UDFLAGS=-m$(MODEL) -conf= -O -release -dip1000 -w -Isrc -Iimport DDOCFLAGS=-conf= -c -w -o- -Isrc -Iimport -version=CoreDdoc +# Unittest flags +UTFLAGS=-version=CoreUnittest -version=druntime_unittest -unittest + #CFLAGS=/O2 /I"$(VCDIR)"\INCLUDE /I"$(SDKDIR)"\Include CFLAGS=/Z7 /I"$(VCDIR)"\INCLUDE /I"$(SDKDIR)"\Include @@ -77,8 +82,7 @@ $(DRUNTIME): $(OBJS) $(SRCS) win64.mak # due to -conf= on the command line, LINKCMD and LIB need to be set in the environment unittest : $(SRCS) $(DRUNTIME) - *"$(DMD)" $(UDFLAGS) -version=druntime_unittest -unittest -ofunittest.exe -main $(SRCS) $(DRUNTIME) -debuglib=$(DRUNTIME) -defaultlib=$(DRUNTIME) user32.lib - unittest + *$(DMD) $(UDFLAGS) $(UTFLAGS) -ofunittest.exe -main $(SRCS) $(DRUNTIME) -debuglib=$(DRUNTIME) -defaultlib=$(DRUNTIME) user32.lib ################### Win32 COFF support ######################### From 82b1bbcea4fb432919a5ad38f9346bc7fb23392e Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 4 Dec 2018 16:28:04 +0200 Subject: [PATCH 19/22] Mark isShared as the msb inside the ref count --- src/core/experimental/array.d | 133 +++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 25 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index cdce40cdec..448dec41f3 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -448,13 +448,46 @@ struct rcarray(T) private static enum double capacityFactor = 3.0 / 2; private static enum initCapacity = 3; - private bool isShared; + //private bool isShared; + + private static enum isSharedMask = 1UL << ((PrefixAllocator.prefixSize * 8) - 1); + + private @nogc nothrow pure @safe + bool isShared() const + { + return opCmpPrefix!">="(support, isSharedMask); + } + + private @nogc nothrow pure @trusted + void setIsShared(T)(const T[] _support, bool _isShared) const + { + static size_t _sharedOpCmpPrefix(string op, T)(const T[] _support, size_t val) + { + return cast(size_t)(atomicOp!op(*cast(shared size_t *)&sharedAllocator.prefix(_support), val)); + } + + static size_t _sharedOpPrefix(string op, T)(const T[] _support, size_t val) + { + return cast(size_t)(atomicOp!op(*cast(shared size_t *)&sharedAllocator.prefix(_support), val)); + } + + + if (_isShared) + { + //auto t = (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_sharedOpCmpPrefix!("==", T)))(_support, 1); + //assert(t); + cast(void) (cast(size_t function(const T[], size_t) @nogc nothrow pure)(&_sharedOpPrefix!("|=", T)))(_support, isSharedMask); + //atomicOp!op(*cast(shared size_t *)&sharedAllocator.prefix(support), val); + } + } private @trusted auto pref() const { assert(support !is null); - if (isShared) + auto _isShared = true; + //if (isShared) + if (_isShared) { return sharedAllocator.prefix(support); } @@ -467,7 +500,9 @@ struct rcarray(T) private size_t _opPrefix(string op, T)(const T[] _support, size_t val) const { assert(_support !is null); - if (isShared) + auto _isShared = true; + //if (isShared) + if (_isShared) { return cast(size_t)(atomicOp!op(*cast(shared size_t *)&sharedAllocator.prefix(_support), val)); } @@ -485,10 +520,10 @@ struct rcarray(T) } private @nogc nothrow pure @trusted - size_t opCmpPrefix(string op, T)(const T[] _support, size_t val) const + bool opCmpPrefix(string op, T)(const T[] _support, size_t val) const if ((op == "==") || (op == "<=") || (op == "<") || (op == ">=") || (op == ">")) { - return (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(_support, val); + return cast(bool) (cast(size_t delegate(const T[], size_t) const @nogc nothrow pure)(&_opPrefix!(op, T)))(_support, val); } private @nogc nothrow pure @trusted @@ -504,7 +539,8 @@ struct rcarray(T) if (0) { T t = T.init; } assert(_support !is null); - if (opPrefix!("-=")(_support, 1) == 0) + size_t defaultRCVal = isShared * isSharedMask; + if (opPrefix!("-=")(_support, 1) == defaultRCVal) { () @trusted { version (CoreUnittest) @@ -519,7 +555,7 @@ struct rcarray(T) } } - private static string immutableInsert(StuffType)(string stuff) + private static string immutableInsert(StuffType, alias _isShared)(string stuff) { auto stuffLengthStr = q{ size_t stuffLength = } ~ stuff ~ ".length;"; @@ -528,12 +564,12 @@ struct rcarray(T) version (CoreUnittest) { - void[] tmpSupport = (() @trusted => pureAllocate(isShared, stuffLength * stateSize!T))(); + void[] tmpSupport = (() @trusted => pureAllocate(_isShared, stuffLength * stateSize!T))(); } else { void[] tmpSupport; - if (isShared) + if (_isShared) { tmpSupport = (() @trusted => sharedAllocator.allocate(stuffLength * stateSize!T))(); } @@ -584,8 +620,17 @@ struct rcarray(T) { static if (is(Q == immutable) || is(Q == const)) { - isShared = true; - mixin(immutableInsert!(typeof(values))("values")); + static if (is(Q == immutable)) + { + bool _isShared = true; + mixin(immutableInsert!(typeof(values), true)("values")); + setIsShared(support, true); + } + else + { + bool _isShared = false; + mixin(immutableInsert!(typeof(values), true)("values")); + } } else { @@ -621,7 +666,8 @@ struct rcarray(T) private enum copyCtorIncRef = q{ payload = rhs.payload; support = rhs.support; - isShared = rhs.isShared; + //isShared = rhs.isShared; + setIsShared(support, rhs.isShared); if (support !is null) { @@ -656,14 +702,20 @@ struct rcarray(T) this(ref typeof(this) rhs) immutable { - isShared = true; - mixin(immutableInsert!(typeof(rhs))("rhs")); + bool _isShared = true; + mixin(immutableInsert!(typeof(rhs), true)("rhs")); + //isShared = true; + setIsShared(support, true); } this(const ref typeof(this) rhs) immutable { - isShared = rhs.isShared; - mixin(immutableInsert!(typeof(rhs.payload))("rhs.payload")); + // TODO should infer rhs.isShared + bool _isShared = true; + mixin(immutableInsert!(typeof(rhs.payload), true)("rhs.payload")); + //isShared = rhs.isShared; + setIsShared(support, true); + //setIsShared(support, rhs.isShared); } this(immutable ref typeof(this) rhs) immutable @@ -682,7 +734,8 @@ struct rcarray(T) { support = _support; payload = _payload; - isShared = _isShared; + //isShared = _isShared; + setIsShared(support, _isShared); if (support !is null) { addRef(support); @@ -881,7 +934,9 @@ struct rcarray(T) version (CoreUnittest) { - auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(isShared, n * stateSize!T)))(); + // TODO ok? + auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(false, n * stateSize!T)))(); + //auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(isShared, n * stateSize!T)))(); } else { @@ -1696,6 +1751,29 @@ struct rcarray(T) version (CoreUnittest) { // Begin CoreUnittest - conditional compilation so we can check for mem leaks +template CommonType(T...) +{ + static if (is(typeof(T[0]))) + { + alias CommonType = typeof(T[0]); + } + else + { + alias CommonType = T[0]; + } +} + +CommonType!T[T.length] staticArray(T...)(T args) +if (is(CommonType!T)) +{ + return [args]; +} + +unittest { + auto a = staticArray(1,2,3,4); + static assert(is(typeof(a) == int[4])); +} + private nothrow pure @safe void testConcatAndAppend() { @@ -1900,26 +1978,28 @@ void testImmutability() assert(a[0] == 1); assert(a5 == [2, 2, 3]); + enum isSharedMask = 1UL << ((PrefixAllocator.prefixSize * 8) - 1); + // Create immtable copies from mutable, const and immutable { auto aa = rcarray!(int)(1, 2, 3); auto aa2 = aa.idup(); assert(aa.opCmpPrefix!"=="(aa.support, 1)); - assert(aa2.opCmpPrefix!"=="(aa2.support, 1)); + assert(aa2.opCmpPrefix!"=="(aa2.support, (1 | isSharedMask))); } { auto aa = const rcarray!(int)(1, 2, 3); auto aa2 = aa.idup(); assert(aa.opCmpPrefix!"=="(aa.support, 1)); - assert(aa2.opCmpPrefix!"=="(aa2.support, 1)); + assert(aa2.opCmpPrefix!"=="(aa2.support, (1 | isSharedMask))); } { auto aa = immutable rcarray!(int)(1, 2, 3); auto aa2 = aa.idup(); - assert(aa.opCmpPrefix!"=="(aa.support, 2)); - assert(aa2.opCmpPrefix!"=="(aa2.support, 2)); + assert(aa.opCmpPrefix!"=="(aa.support, (2 | isSharedMask))); + assert(aa2.opCmpPrefix!"=="(aa2.support, (2 | isSharedMask))); } } @@ -1929,7 +2009,9 @@ void testConstness() auto a = const rcarray!(int)(1, 2, 3); auto a2 = a; immutable rcarray!int a5 = a; - assert(a5.opCmpPrefix!"=="(a5.support, 1)); + enum isSharedMask = 1UL << ((PrefixAllocator.prefixSize * 8) - 1); + + assert(a5.opCmpPrefix!"=="(a5.support, (1 | isSharedMask))); assert(a.opCmpPrefix!"=="(a.support, 2)); assert(a2[0] == 1); @@ -1955,6 +2037,7 @@ void testConstness() private nothrow pure @safe void testWithStruct() { + enum isSharedMask = 1UL << ((PrefixAllocator.prefixSize * 8) - 1); auto array = rcarray!int(1, 2, 3); { assert(array.opCmpPrefix!"=="(array.support, 1)); @@ -1974,8 +2057,8 @@ void testWithStruct() assert(array.opCmpPrefix!"=="(array.support, 2)); array[0] = 3; assert(immArrayOfArrays[0][0] == 2); - assert(immArrayOfArrays.opCmpPrefix!"=="(immArrayOfArrays.support, 1)); - assert(immArrayOfArrays[0].opCmpPrefix!"=="(immArrayOfArrays[0].support, 1)); + assert(immArrayOfArrays.opCmpPrefix!"=="(immArrayOfArrays.support, (1 | isSharedMask))); + assert(immArrayOfArrays[0].opCmpPrefix!"=="(immArrayOfArrays[0].support, (1 | isSharedMask))); static assert(!__traits(compiles, { immArrayOfArrays[0][0] = 2; })); static assert(!__traits(compiles, { immArrayOfArrays[0] = array; })); } From ab1eef72e2ceb83818ad068c0bac1291872f34cb Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Tue, 4 Dec 2018 16:29:00 +0200 Subject: [PATCH 20/22] Leave bounds checking to underlying T[] support --- src/core/experimental/array.d | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 448dec41f3..0b3ad118f6 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -1263,11 +1263,6 @@ struct rcarray(T) */ Qualified opSlice(this Qualified)(size_t start, size_t end) { - version (D_NoBoundsChecks) { } else - { - assert(start <= end && end <= length, - "Array.opSlice(s, e): Invalid bounds: Ensure start <= end <= length"); - } return typeof(this)(support, payload[start .. end], isShared); } @@ -1295,10 +1290,6 @@ struct rcarray(T) */ ref auto opIndex(this _)(size_t idx) { - version (D_NoBoundsChecks) { } else - { - assert(idx < length, "Array.opIndex: Index out of bounds"); - } return payload[idx]; } @@ -1324,10 +1315,6 @@ struct rcarray(T) */ ref auto opIndexUnary(string op)(size_t idx) { - version (D_NoBoundsChecks) { } else - { - assert(idx < length, "Array.opIndexUnary!" ~ op ~ ": Index out of bounds"); - } mixin("return " ~ op ~ "payload[idx];"); } @@ -1357,10 +1344,6 @@ struct rcarray(T) ref auto opIndexAssign(U)(U elem, size_t idx) if (is(U : T)) { - version (D_NoBoundsChecks) { } else - { - assert(idx < length, "Array.opIndexAssign: Index out of bounds"); - } return payload[idx] = elem; } @@ -1419,11 +1402,6 @@ struct rcarray(T) auto opSliceAssign(U)(U elem, size_t start, size_t end) if (is(U : T)) { - version (D_NoBoundsChecks) { } else - { - assert(start <= end, "Array.opSliceAssign: Index out of bounds"); - assert(end < length, "Array.opSliceAssign: Index out of bounds"); - } return payload[start .. end] = elem; } @@ -1453,10 +1431,6 @@ struct rcarray(T) ref auto opIndexOpAssign(string op, U)(U elem, size_t idx) if (is(U : T)) { - version (D_NoBoundsChecks) { } else - { - assert(idx < length, "Array.opIndexOpAssign!" ~ op ~ ": Index out of bounds"); - } mixin("return payload[idx]" ~ op ~ "= elem;"); } From 8e2a813bd6e07ca071c7fc40363410c80ecf4ddd Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Thu, 6 Dec 2018 18:21:57 +0200 Subject: [PATCH 21/22] Make array usable in betterC --- src/core/experimental/array.d | 51 ++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 0b3ad118f6..4b2c32c209 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -555,9 +555,9 @@ struct rcarray(T) } } - private static string immutableInsert(StuffType, alias _isShared)(string stuff) + private static string immutableInsert(StuffType, alias _isShared, string stuff)() { - auto stuffLengthStr = q{ + enum stuffLengthStr = q{ size_t stuffLength = } ~ stuff ~ ".length;"; return stuffLengthStr ~ q{ @@ -591,7 +591,11 @@ struct rcarray(T) ~"}" ~q{ - support = (() @trusted => cast(typeof(support))(tmpSupport))(); + // In order to support D_BetterC, we need to cast the `void[]` to `T.ptr` + // and then manually pass the length information again. This way we avoid + // calling druntime's `_d_arraycast`, which is called whenever we cast between + // two dynamic arrays. + support = (() @trusted => (cast(typeof(support.ptr))(tmpSupport.ptr))[0 .. stuffLength])(); payload = (() @trusted => cast(typeof(payload))(support[0 .. stuffLength]))(); if (support) addRef(support); }; @@ -623,13 +627,13 @@ struct rcarray(T) static if (is(Q == immutable)) { bool _isShared = true; - mixin(immutableInsert!(typeof(values), true)("values")); + mixin(immutableInsert!(typeof(values), true, "values")()); setIsShared(support, true); } else { bool _isShared = false; - mixin(immutableInsert!(typeof(values), true)("values")); + mixin(immutableInsert!(typeof(values), true, "values")()); } } else @@ -702,20 +706,18 @@ struct rcarray(T) this(ref typeof(this) rhs) immutable { - bool _isShared = true; - mixin(immutableInsert!(typeof(rhs), true)("rhs")); + bool _isShared = true; + mixin(immutableInsert!(typeof(rhs), true, "rhs")()); //isShared = true; setIsShared(support, true); } this(const ref typeof(this) rhs) immutable { - // TODO should infer rhs.isShared - bool _isShared = true; - mixin(immutableInsert!(typeof(rhs.payload), true)("rhs.payload")); - //isShared = rhs.isShared; + bool _isShared = true; + mixin(immutableInsert!(typeof(rhs.payload), true, "rhs.payload")()); + //isShared = true; setIsShared(support, true); - //setIsShared(support, rhs.isShared); } this(immutable ref typeof(this) rhs) immutable @@ -934,13 +936,22 @@ struct rcarray(T) version (CoreUnittest) { - // TODO ok? - auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(false, n * stateSize!T)))(); - //auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(isShared, n * stateSize!T)))(); + version(D_BetterC) + { + Unqual!T[] tmpSupport = (() @trusted => (cast(Unqual!T*)(pureAllocate(false, n * stateSize!T).ptr))[0 .. n])(); + } + else + { + auto tmpSupport = (() @trusted => cast(Unqual!T[])(pureAllocate(false, n * stateSize!T)))(); + } } else { - auto tmpSupport = (() @trusted => cast(Unqual!T[])(localAllocator.allocate(n * stateSize!T)))(); + // In order to support D_BetterC, we need to cast the `void[]` to `T.ptr` + // and then manually pass the length information again. This way we avoid + // calling druntime's `_d_arraycast`, which is called whenever we cast between + // two dynamic arrays. + Unqual!T[] tmpSupport = (() @trusted => (cast(Unqual!T*)(localAllocator.allocate(n * stateSize!T).ptr))[0 .. n])(); } assert(tmpSupport !is null); for (size_t i = 0; i < tmpSupport.length; ++i) @@ -1705,6 +1716,8 @@ struct rcarray(T) assert(arr3 > arr4); } + version(D_BetterC) { } else + { /// auto toHash() { @@ -1720,6 +1733,7 @@ struct rcarray(T) assert(arr1.toHash == rcarray!int(1, 2, 3).toHash); assert(rcarray!int().toHash == rcarray!int().toHash); } + } } version (CoreUnittest) @@ -2049,6 +2063,9 @@ void testWithStruct() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } +version(D_BetterC) { } else +{ // Can't test with class in D_BetterC + private nothrow pure @safe void testWithClass() { @@ -2081,6 +2098,8 @@ void testWithClass() assert(sharedAllocator.bytesUsed == 0, "Array ref count leaks memory"); } +} // Can't test with class in D_BetterC + private @nogc nothrow pure @safe void testOpOverloads() { From ee9679682b82db34da95433f9cdaa0e9e1dfb898 Mon Sep 17 00:00:00 2001 From: Eduard Staniloiu Date: Fri, 29 Mar 2019 15:16:46 +0200 Subject: [PATCH 22/22] Fix invalid support bug --- src/core/experimental/array.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/experimental/array.d b/src/core/experimental/array.d index 4b2c32c209..6ab0a7b9e0 100644 --- a/src/core/experimental/array.d +++ b/src/core/experimental/array.d @@ -671,10 +671,10 @@ struct rcarray(T) payload = rhs.payload; support = rhs.support; //isShared = rhs.isShared; - setIsShared(support, rhs.isShared); if (support !is null) { + setIsShared(support, rhs.isShared); addRef(support); } };