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