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