| /** | |
| 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: Tret_El[], Tret_El, Tarr...)(auto ref Tarr froms) @trusted | |
| { | |
| import core.exception : onOutOfMemoryError; | |
| import core.internal.array.utils : __arrayAlloc; | |
| 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]); // Element type of the result array | |
| alias UnqT = Unqual!T; // Unqualified version of T (strips const/immutable) | |
| enum elemSize = T.sizeof; // Size of each element | |
| enum hasPostblit = __traits(hasPostblit, T); // Whether T has a postblit constructor | |
| // Compute total length of the resulting array | |
| static foreach (from; froms) | |
| static if (is(typeof(from) : T)) | |
| totalLen++; // Single element contributes one to length | |
| else | |
| totalLen += from.length; // Arrays contribute their full length | |
| if (totalLen == 0) | |
| return res; // Return an empty array if no elements are present | |
| // Allocate memory for mutable arrays using __arrayAlloc | |
| res = (cast(Tret_El*) &__arrayAlloc!(UnqT)(elemSize * totalLen)[0])[0 .. totalLen]; | |
| if (res.ptr is null) | |
| onOutOfMemoryError(); // Abort if allocation fails | |
| 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(UnqT *) res; | |
| foreach (ref from; froms) | |
| static if (is (typeof(from) : T)) | |
| memcpy(resptr++, cast(UnqT *) &from, elemSize); | |
| else | |
| { | |
| const len = from.length; | |
| if (len) | |
| { | |
| memcpy(resptr, cast(UnqT *) 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", "_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!"); | |
| } | |
| } |