| /** | |
| This module contains support for controlling dynamic arrays' concatenation | |
| Copyright: Copyright Digital Mars 2000 - 2019. | |
| License: Distributed under the | |
| $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). | |
| (See accompanying file LICENSE) | |
| Source: $(DRUNTIMESRC core/internal/_array/_concatenation.d) | |
| */ | |
| module core.internal.array.concatenation; | |
| /** | |
| * Concatenate the arrays inside of `froms`. | |
| * `_d_arraycatnTX(a, b, c)` means `a ~ b ~ c`. | |
| * | |
| * Params: | |
| * froms = Arrays to be concatenated. | |
| * Returns: | |
| * A newly allocated array that contains all the elements from `froms`. | |
| */ | |
| Tret _d_arraycatnTX(Tret, Tarr...)(auto ref Tarr froms) @trusted | |
| { | |
| import core.internal.array.capacity : _d_arraysetlengthTImpl; | |
| import core.internal.traits : hasElaborateCopyConstructor, Unqual; | |
| import core.lifetime : copyEmplace; | |
| import core.stdc.string : memcpy; | |
| Tret res; | |
| size_t totalLen; | |
| alias T = typeof(res[0]); | |
| enum elemSize = T.sizeof; | |
| enum hasPostblit = __traits(hasPostblit, T); | |
| static foreach (from; froms) | |
| static if (is (typeof(from) : T)) | |
| totalLen++; | |
| else | |
| totalLen += from.length; | |
| if (totalLen == 0) | |
| return res; | |
| // We cannot use this, because it refuses to work if the array type has disabled default construction. | |
| // res.length = totalLen; | |
| // Call the runtime function directly instead. | |
| // TODO: once `__arrayAlloc` is templated, call that instead. | |
| version (D_ProfileGC) | |
| { | |
| // TODO: forward file, line, name from _d_arraycatnTXTrace | |
| _d_arraysetlengthTImpl!(typeof(res))._d_arraysetlengthTTrace( | |
| res, totalLen, __FILE__, __LINE__, __FUNCTION__); | |
| } | |
| else | |
| { | |
| _d_arraysetlengthTImpl!(typeof(res))._d_arraysetlengthT(res, totalLen); | |
| } | |
| /* Currently, if both a postblit and a cpctor are defined, the postblit is | |
| * used. If this changes, the condition below will have to be adapted. | |
| */ | |
| static if (hasElaborateCopyConstructor!T && !hasPostblit) | |
| { | |
| size_t i = 0; | |
| foreach (ref from; froms) | |
| static if (is(typeof(from) : T)) | |
| copyEmplace(cast(T) from, res[i++]); | |
| else | |
| { | |
| if (from.length) | |
| foreach (ref elem; from) | |
| copyEmplace(cast(T) elem, res[i++]); | |
| } | |
| } | |
| else | |
| { | |
| auto resptr = cast(Unqual!T *) res; | |
| foreach (ref from; froms) | |
| static if (is (typeof(from) : T)) | |
| memcpy(resptr++, cast(Unqual!T *) &from, elemSize); | |
| else | |
| { | |
| const len = from.length; | |
| if (len) | |
| { | |
| memcpy(resptr, cast(Unqual!T *) from, len * elemSize); | |
| resptr += len; | |
| } | |
| } | |
| static if (hasPostblit) | |
| foreach (ref elem; res) | |
| (cast() elem).__xpostblit(); | |
| } | |
| return res; | |
| } | |
| // postblit | |
| @safe unittest | |
| { | |
| int counter; | |
| struct S | |
| { | |
| int val; | |
| this(this) | |
| { | |
| counter++; | |
| } | |
| } | |
| S[] arr1 = [S(0), S(1), S(2)]; | |
| S[] arr2 = []; | |
| S[] arr3 = [S(6), S(7), S(8)]; | |
| S elem = S(9); | |
| S[] result = _d_arraycatnTX!(S[])(arr1, arr2, arr3, elem); | |
| assert(counter == 7); | |
| assert(result == [S(0), S(1), S(2), S(6), S(7), S(8), S(9)]); | |
| } | |
| // copy constructor | |
| @safe unittest | |
| { | |
| int counter; | |
| struct S | |
| { | |
| int val; | |
| this(ref return scope S rhs) | |
| { | |
| val = rhs.val; | |
| counter++; | |
| } | |
| } | |
| S[] arr1 = [S(0), S(1), S(2)]; | |
| S[] arr2 = [S(3), S(4), S(5)]; | |
| S[] arr3 = [S(6), S(7), S(8)]; | |
| S elem = S(9); | |
| S[] result = _d_arraycatnTX!(S[])(arr1, elem, arr2, arr3); | |
| assert(counter == 10); | |
| assert(result == [S(0), S(1), S(2), S(9), S(3), S(4), S(5), S(6), S(7), S(8)]); | |
| } | |
| // throwing | |
| @safe unittest | |
| { | |
| int counter; | |
| bool didThrow; | |
| struct S | |
| { | |
| int val; | |
| this(this) | |
| { | |
| counter++; | |
| if (counter == 4) | |
| throw new Exception(""); | |
| } | |
| } | |
| try | |
| { | |
| S[] arr1 = [S(0), S(1), S(2)]; | |
| S[] arr2 = [S(3), S(4), S(5)]; | |
| _d_arraycatnTX!(S[])(arr1, arr2); | |
| } | |
| catch (Exception) | |
| { | |
| didThrow = true; | |
| } | |
| assert(counter == 4); | |
| assert(didThrow); | |
| } | |
| version (D_ProfileGC) | |
| { | |
| /** | |
| * TraceGC wrapper around $(REF _d_arraycatnTX, core,internal,array,concatenation). | |
| */ | |
| Tret _d_arraycatnTXTrace(Tret, Tarr...)(scope auto ref Tarr froms, string file = __FILE__, int line = __LINE__, string funcname = __FUNCTION__) @trusted | |
| { | |
| version (D_TypeInfo) | |
| { | |
| import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure; | |
| mixin(TraceHook!(Tarr.stringof, "_d_arraycatnTX")); | |
| import core.lifetime: forward; | |
| return _d_arraycatnTX!Tret(forward!froms); | |
| } | |
| else | |
| assert(0, "Cannot concatenate arrays if compiling without support for runtime type information!"); | |
| } | |
| } |