1/**
2 This module contains support for controlling dynamic arrays' appending
3
4  Copyright: Copyright Digital Mars 2000 - 2019.
5  License: Distributed under the
6       $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7     (See accompanying file LICENSE)
8  Source: $(DRUNTIMESRC core/_internal/_array/_appending.d)
9*/
10module core.internal.array.appending;
11
12/// See $(REF _d_arrayappendcTX, rt,lifetime,_d_arrayappendcTX)
13private extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref return scope byte[] px, size_t n) @trusted pure nothrow;
14
15private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lhs = rhs; });
16
17/// Implementation of `_d_arrayappendcTX` and `_d_arrayappendcTXTrace`
18template _d_arrayappendcTXImpl(Tarr : T[], T)
19{
20    import core.internal.array.utils : _d_HookTraceImpl;
21
22    private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!";
23
24    /**
25     * Extend an array `px` by `n` elements.
26     * Caller must initialize those elements.
27     * Params:
28     *  px = the array that will be extended, taken as a reference
29     *  n = how many new elements to extend it with
30     * Returns:
31     *  The new value of `px`
32     * Bugs:
33    *   This function template was ported from a much older runtime hook that bypassed safety,
34    *   purity, and throwabilty checks. To prevent breaking existing code, this function template
35    *   is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
36     */
37    static if (isCopyingNothrow!T) // `nothrow` deduction doesn't work, so this is needed
38        ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow
39        {
40            pragma(inline, false);
41
42            mixin(_d_arrayappendcTXBody);
43        }
44    else
45        ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow
46        {
47            pragma(inline, false);
48
49            mixin(_d_arrayappendcTXBody);
50        }
51
52    private enum _d_arrayappendcTXBody = q{
53        version (D_TypeInfo)
54        {
55            auto ti = typeid(Tarr);
56
57            // _d_arrayappendcTX takes the `px` as a ref byte[], but its length
58            // should still be the original length
59            auto pxx = (cast(byte*)px.ptr)[0 .. px.length];
60            ._d_arrayappendcTX(ti, pxx, n);
61            px = (cast(T*)pxx.ptr)[0 .. pxx.length];
62
63            return px;
64        }
65        else
66            assert(0, "Cannot append arrays if compiling without support for runtime type information!");
67    };
68
69    /**
70     * TraceGC wrapper around $(REF _d_arrayappendcTX, rt,array,appending,_d_arrayappendcTXImpl).
71     * Bugs:
72     *  This function template was ported from a much older runtime hook that bypassed safety,
73     *  purity, and throwabilty checks. To prevent breaking existing code, this function template
74     *  is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
75     */
76    alias _d_arrayappendcTXTrace = _d_HookTraceImpl!(Tarr, _d_arrayappendcTX, errorMessage);
77}
78
79/// Implementation of `_d_arrayappendT` and `_d_arrayappendTTrace`
80template _d_arrayappendTImpl(Tarr : T[], T)
81{
82    import core.internal.array.utils : _d_HookTraceImpl;
83
84    private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!";
85
86    /**
87     * Append array `y` to array `x`.
88     * Params:
89     *  x = what array to append to, taken as a reference
90     *  y = what should be appended
91     * Returns:
92     *  The new value of `x`
93     * Bugs:
94    *   This function template was ported from a much older runtime hook that bypassed safety,
95    *   purity, and throwabilty checks. To prevent breaking existing code, this function template
96    *   is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
97     */
98    static if (isCopyingNothrow!T)
99        ref Tarr _d_arrayappendT(return ref scope Tarr x, scope Tarr y) @trusted pure nothrow
100        {
101            pragma(inline, false);
102
103            mixin(_d_arrayappendTBody);
104        }
105    else
106        ref Tarr _d_arrayappendT(return ref scope Tarr x, scope Tarr y) @trusted pure
107        {
108            pragma(inline, false);
109
110            mixin(_d_arrayappendTBody);
111        }
112
113    private enum _d_arrayappendTBody = q{
114        import core.stdc.string : memcpy;
115        import core.internal.traits : hasElaborateCopyConstructor, Unqual;
116        import core.lifetime : copyEmplace;
117
118        auto length = x.length;
119
120        _d_arrayappendcTXImpl!Tarr._d_arrayappendcTX(x, y.length);
121
122        static if (hasElaborateCopyConstructor!T)
123        {
124            foreach (i; 0 .. y.length)
125                copyEmplace(y[i], x[length + i]);
126        }
127        else
128        {
129            // blit all elements at once
130            if (y.length)
131                memcpy(cast(Unqual!T *)&x[length], cast(Unqual!T *)&y[0], y.length * T.sizeof);
132        }
133
134        return x;
135    };
136
137    /**
138     * TraceGC wrapper around $(REF _d_arrayappendT, rt,array,appending,_d_arrayappendTImpl).
139     * Bugs:
140     *  This function template was ported from a much older runtime hook that bypassed safety,
141     *  purity, and throwabilty checks. To prevent breaking existing code, this function template
142     *  is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
143     */
144    alias _d_arrayappendTTrace = _d_HookTraceImpl!(Tarr, _d_arrayappendT, errorMessage);
145}
146
147@safe unittest
148{
149    double[] arr1;
150    foreach (i; 0 .. 4)
151        _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, [cast(double)i]);
152    assert(arr1 == [0.0, 1.0, 2.0, 3.0]);
153}
154
155@safe unittest
156{
157    int blitted;
158    struct Item
159    {
160        this(this)
161        {
162            blitted++;
163        }
164    }
165
166    Item[] arr1 = [Item(), Item()];
167    Item[] arr2 = [Item(), Item()];
168    Item[] arr1_org = [Item(), Item()];
169    arr1_org ~= arr2;
170    _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);
171
172    // postblit should have triggered on at least the items in arr2
173    assert(blitted >= arr2.length);
174}
175
176@safe nothrow unittest
177{
178    int blitted;
179    struct Item
180    {
181        this(this) nothrow
182        {
183            blitted++;
184        }
185    }
186
187    Item[][] arr1 = [[Item()]];
188    Item[][] arr2 = [[Item()]];
189
190    _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);
191
192    // no postblit should have happened because arr{1,2} contain dynamic arrays
193    assert(blitted == 0);
194}
195
196@safe nothrow unittest
197{
198    int copied;
199    struct Item
200    {
201        this(const scope ref Item) nothrow
202        {
203            copied++;
204        }
205    }
206
207    Item[1][] arr1 = [[Item()]];
208    Item[1][] arr2 = [[Item()]];
209
210    _d_arrayappendTImpl!(typeof(arr1))._d_arrayappendT(arr1, arr2);
211    // copy constructor should have been invoked because arr{1,2} contain static arrays
212    assert(copied >= arr2.length);
213}
214
215@safe nothrow unittest
216{
217    string str;
218    _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "a");
219    _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "b");
220    _d_arrayappendTImpl!(typeof(str))._d_arrayappendT(str, "c");
221    assert(str == "abc");
222}
223