]> git.ipfire.org Git - people/ms/gcc.git/blob - libphobos/libdruntime/core/internal/array/appending.d
d: Merge upstream dmd, druntime 4ca4140e58, phobos 454dff14d.
[people/ms/gcc.git] / libphobos / libdruntime / core / internal / array / appending.d
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 private enum errorMessage = "Cannot append to array if compiling without support for runtime type information!";
21
22 /**
23 * Extend an array `px` by `n` elements.
24 * Caller must initialize those elements.
25 * Params:
26 * px = the array that will be extended, taken as a reference
27 * n = how many new elements to extend it with
28 * Returns:
29 * The new value of `px`
30 * Bugs:
31 * This function template was ported from a much older runtime hook that bypassed safety,
32 * purity, and throwabilty checks. To prevent breaking existing code, this function template
33 * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
34 */
35 ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow
36 {
37 // needed for CTFE: https://github.com/dlang/druntime/pull/3870#issuecomment-1178800718
38 pragma(inline, false);
39 version (D_TypeInfo)
40 {
41 auto ti = typeid(Tarr);
42
43 // _d_arrayappendcTX takes the `px` as a ref byte[], but its length
44 // should still be the original length
45 auto pxx = (cast(byte*)px.ptr)[0 .. px.length];
46 ._d_arrayappendcTX(ti, pxx, n);
47 px = (cast(T*)pxx.ptr)[0 .. pxx.length];
48
49 return px;
50 }
51 else
52 assert(0, errorMessage);
53 }
54
55 version (D_ProfileGC)
56 {
57 import core.internal.array.utils : _d_HookTraceImpl;
58
59 /**
60 * TraceGC wrapper around $(REF _d_arrayappendcTX, rt,array,appending,_d_arrayappendcTXImpl).
61 * Bugs:
62 * This function template was ported from a much older runtime hook that bypassed safety,
63 * purity, and throwabilty checks. To prevent breaking existing code, this function template
64 * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations.
65 */
66 alias _d_arrayappendcTXTrace = _d_HookTraceImpl!(Tarr, _d_arrayappendcTX, errorMessage);
67 }
68 }
69
70 /// Implementation of `_d_arrayappendT`
71 ref Tarr _d_arrayappendT(Tarr : T[], T)(return ref scope Tarr x, scope Tarr y) @trusted
72 {
73 pragma(inline, false);
74
75 import core.stdc.string : memcpy;
76 import core.internal.traits : hasElaborateCopyConstructor, Unqual;
77
78 enum hasPostblit = __traits(hasPostblit, T);
79 auto length = x.length;
80
81 _d_arrayappendcTXImpl!Tarr._d_arrayappendcTX(x, y.length);
82
83 // Only call `copyEmplace` if `T` has a copy ctor and no postblit.
84 static if (hasElaborateCopyConstructor!T && !hasPostblit)
85 {
86 import core.lifetime : copyEmplace;
87
88 foreach (i, ref elem; y)
89 copyEmplace(elem, x[length + i]);
90 }
91 else
92 {
93 if (y.length)
94 {
95 // blit all elements at once
96 auto xptr = cast(Unqual!T *)&x[length];
97 immutable size = T.sizeof;
98
99 memcpy(xptr, cast(Unqual!T *)&y[0], y.length * size);
100
101 // call postblits if they exist
102 static if (hasPostblit)
103 {
104 auto eptr = xptr + y.length;
105 for (auto ptr = xptr; ptr < eptr; ptr++)
106 ptr.__xpostblit();
107 }
108 }
109 }
110
111 return x;
112 }
113
114 version (D_ProfileGC)
115 {
116 /**
117 * TraceGC wrapper around $(REF _d_arrayappendT, core,internal,array,appending).
118 */
119 ref Tarr _d_arrayappendTTrace(Tarr : T[], T)(string file, int line, string funcname, return ref scope Tarr x, scope Tarr y) @trusted
120 {
121 version (D_TypeInfo)
122 {
123 import core.internal.array.utils: TraceHook, gcStatsPure, accumulatePure;
124 mixin(TraceHook!(Tarr.stringof, "_d_arrayappendT"));
125
126 return _d_arrayappendT(x, y);
127 }
128 else
129 assert(0, "Cannot append to array if compiling without support for runtime type information!");
130 }
131 }
132
133 @safe unittest
134 {
135 double[] arr1;
136 foreach (i; 0 .. 4)
137 _d_arrayappendT(arr1, [cast(double)i]);
138 assert(arr1 == [0.0, 1.0, 2.0, 3.0]);
139 }
140
141 @safe unittest
142 {
143 int blitted;
144 struct Item
145 {
146 this(this)
147 {
148 blitted++;
149 }
150 }
151
152 Item[] arr1 = [Item(), Item()];
153 Item[] arr2 = [Item(), Item()];
154 Item[] arr1_org = [Item(), Item()];
155 arr1_org ~= arr2;
156 _d_arrayappendT(arr1, arr2);
157
158 // postblit should have triggered on at least the items in arr2
159 assert(blitted >= arr2.length);
160 }
161
162 @safe nothrow unittest
163 {
164 int blitted;
165 struct Item
166 {
167 this(this) nothrow
168 {
169 blitted++;
170 }
171 }
172
173 Item[][] arr1 = [[Item()]];
174 Item[][] arr2 = [[Item()]];
175
176 _d_arrayappendT(arr1, arr2);
177
178 // no postblit should have happened because arr{1,2} contain dynamic arrays
179 assert(blitted == 0);
180 }
181
182 @safe nothrow unittest
183 {
184 int copied;
185 struct Item
186 {
187 this(const scope ref Item) nothrow
188 {
189 copied++;
190 }
191 }
192
193 Item[1][] arr1 = [[Item()]];
194 Item[1][] arr2 = [[Item()]];
195
196 _d_arrayappendT(arr1, arr2);
197 // copy constructor should have been invoked because arr{1,2} contain static arrays
198 assert(copied >= arr2.length);
199 }
200
201 @safe nothrow unittest
202 {
203 string str;
204 _d_arrayappendT(str, "a");
205 _d_arrayappendT(str, "b");
206 _d_arrayappendT(str, "c");
207 assert(str == "abc");
208 }