| /** |
| 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!"); |
| } |
| } |