xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/libdruntime/core/internal/array/appending.d (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
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 */
10 module core.internal.array.appending;
11 
12 /// See $(REF _d_arrayappendcTX, rt,lifetime,_d_arrayappendcTX)
13 private extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref return scope byte[] px, size_t n) @trusted pure nothrow;
14 
15 private enum isCopyingNothrow(T) = __traits(compiles, (ref T rhs) nothrow { T lhs = rhs; });
16 
17 /// Implementation of `_d_arrayappendcTX` and `_d_arrayappendcTXTrace`
18 template _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
_d_arrayappendcTX(return ref scope Tarr px,size_t n)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
_d_arrayappendcTX(return ref scope Tarr px,size_t n)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`
80 template _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)
_d_arrayappendT(return ref scope Tarr x,scope Tarr y)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
_d_arrayappendT(return ref scope Tarr x,scope Tarr y)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     {
thisItem160         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     {
thisItem181         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     {
thisItem201         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