1 /** 2 * Implementation of array assignment support routines. 3 * 4 * 5 * Copyright: Copyright Digital Mars 2010 - 2016. 6 * License: Distributed under the 7 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 8 * Authors: Walter Bright, Kenji Hara 9 * Source: $(DRUNTIMESRC src/rt/_arrayassign.d) 10 */ 11 12 module rt.arrayassign; 13 14 private 15 { 16 import rt.util.array; 17 import core.stdc.string; 18 import core.stdc.stdlib; 19 debug(PRINTF) import core.stdc.stdio; 20 } 21 22 /** 23 * Keep for backward binary compatibility. This function can be removed in the future. 24 */ 25 extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to) 26 { 27 debug(PRINTF) printf("_d_arrayassign(from = %p,%d, to = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, ti.tsize); 28 29 immutable elementSize = ti.tsize; 30 31 // Need a temporary buffer tmp[] big enough to hold one element 32 void[16] buf = void; 33 void* ptmp = (elementSize > buf.sizeof) ? malloc(elementSize) : buf.ptr; 34 scope (exit) 35 { 36 if (ptmp != buf.ptr) 37 free(ptmp); 38 } 39 return _d_arrayassign_l(ti, from, to, ptmp); 40 } 41 42 /** 43 * Does array assignment (not construction) from another 44 * lvalue array of the same element type. 45 * Handles overlapping copies. 46 * Input: 47 * ti TypeInfo of the element type. 48 * dst Points target memory. Its .length is equal to the element count, not byte length. 49 * src Points source memory. Its .length is equal to the element count, not byte length. 50 * ptmp Temporary memory for element swapping. 51 */ 52 extern (C) void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp) 53 { 54 debug(PRINTF) printf("_d_arrayassign_l(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize); 55 56 immutable elementSize = ti.tsize; 57 58 enforceRawArraysConformable("copy", elementSize, src, dst, true); 59 60 if (src.ptr < dst.ptr && dst.ptr < src.ptr + elementSize * src.length) 61 { 62 // If dst is in the middle of src memory, use reverse order. 63 for (auto i = dst.length; i--; ) 64 { 65 void* pdst = dst.ptr + i * elementSize; 66 void* psrc = src.ptr + i * elementSize; 67 memcpy(ptmp, pdst, elementSize); 68 memcpy(pdst, psrc, elementSize); 69 ti.postblit(pdst); 70 ti.destroy(ptmp); 71 } 72 } 73 else 74 { 75 // Otherwise, use normal order. 76 foreach (i; 0 .. dst.length) 77 { 78 void* pdst = dst.ptr + i * elementSize; 79 void* psrc = src.ptr + i * elementSize; 80 memcpy(ptmp, pdst, elementSize); 81 memcpy(pdst, psrc, elementSize); 82 ti.postblit(pdst); 83 ti.destroy(ptmp); 84 } 85 } 86 return dst; 87 } 88 89 unittest // Bugzilla 14024 90 { 91 string op; 92 93 struct S 94 { 95 char x = 'x'; 96 this(this) { op ~= x-0x20; } // upper case 97 ~this() { op ~= x; } // lower case 98 } 99 100 S[4] mem; 101 ref S[2] slice(int a, int b) { return mem[a .. b][0 .. 2]; } 102 103 op = null; 104 mem[0].x = 'a'; 105 mem[1].x = 'b'; 106 mem[2].x = 'x'; 107 mem[3].x = 'y'; 108 slice(0, 2) = slice(2, 4); // [ab] = [xy] 109 assert(op == "XaYb", op); 110 111 op = null; 112 mem[0].x = 'x'; 113 mem[1].x = 'y'; 114 mem[2].x = 'a'; 115 mem[3].x = 'b'; 116 slice(2, 4) = slice(0, 2); // [ab] = [xy] 117 assert(op == "XaYb", op); 118 119 op = null; 120 mem[0].x = 'a'; 121 mem[1].x = 'b'; 122 mem[2].x = 'c'; 123 slice(0, 2) = slice(1, 3); // [ab] = [bc] 124 assert(op == "BaCb", op); 125 126 op = null; 127 mem[0].x = 'x'; 128 mem[1].x = 'y'; 129 mem[2].x = 'z'; 130 slice(1, 3) = slice(0, 2); // [yz] = [xy] 131 assert(op == "YzXy", op); 132 } 133 134 /** 135 * Does array assignment (not construction) from another 136 * rvalue array of the same element type. 137 * Input: 138 * ti TypeInfo of the element type. 139 * dst Points target memory. Its .length is equal to the element count, not byte length. 140 * src Points source memory. Its .length is equal to the element count, not byte length. 141 * It is always allocated on stack and never overlapping with dst. 142 * ptmp Temporary memory for element swapping. 143 */ 144 extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp) 145 { 146 debug(PRINTF) printf("_d_arrayassign_r(src = %p,%d, dst = %p,%d) size = %d\n", src.ptr, src.length, dst.ptr, dst.length, ti.tsize); 147 148 immutable elementSize = ti.tsize; 149 150 enforceRawArraysConformable("copy", elementSize, src, dst, false); 151 152 // Always use normal order, because we can assume that 153 // the rvalue src has no overlapping with dst. 154 foreach (i; 0 .. dst.length) 155 { 156 void* pdst = dst.ptr + i * elementSize; 157 void* psrc = src.ptr + i * elementSize; 158 memcpy(ptmp, pdst, elementSize); 159 memcpy(pdst, psrc, elementSize); 160 ti.destroy(ptmp); 161 } 162 return dst; 163 } 164 165 /** 166 * Does array initialization (not assignment) from another 167 * array of the same element type. 168 * ti is the element type. 169 */ 170 extern (C) void[] _d_arrayctor(TypeInfo ti, void[] from, void[] to) 171 { 172 debug(PRINTF) printf("_d_arrayctor(from = %p,%d, to = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, ti.tsize); 173 174 175 auto element_size = ti.tsize; 176 177 enforceRawArraysConformable("initialization", element_size, from, to); 178 179 size_t i; 180 try 181 { 182 for (i = 0; i < to.length; i++) 183 { 184 // Copy construction is defined as bit copy followed by postblit. 185 memcpy(to.ptr + i * element_size, from.ptr + i * element_size, element_size); 186 ti.postblit(to.ptr + i * element_size); 187 } 188 } 189 catch (Throwable o) 190 { 191 /* Destroy, in reverse order, what we've constructed so far 192 */ 193 while (i--) 194 { 195 ti.destroy(to.ptr + i * element_size); 196 } 197 198 throw o; 199 } 200 return to; 201 } 202 203 204 /** 205 * Do assignment to an array. 206 * p[0 .. count] = value; 207 */ 208 extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti) 209 { 210 void* pstart = p; 211 212 auto element_size = ti.tsize; 213 214 // Need a temporary buffer tmp[] big enough to hold one element 215 immutable maxAllocaSize = 512; 216 void *ptmp = (element_size > maxAllocaSize) ? malloc(element_size) : alloca(element_size); 217 218 foreach (i; 0 .. count) 219 { 220 memcpy(ptmp, p, element_size); 221 memcpy(p, value, element_size); 222 ti.postblit(p); 223 ti.destroy(ptmp); 224 p += element_size; 225 } 226 if (element_size > maxAllocaSize) 227 free(ptmp); 228 return pstart; 229 } 230 231 /** 232 * Do construction of an array. 233 * ti[count] p = value; 234 */ 235 extern (C) void* _d_arraysetctor(void* p, void* value, int count, TypeInfo ti) 236 { 237 void* pstart = p; 238 auto element_size = ti.tsize; 239 240 try 241 { 242 foreach (i; 0 .. count) 243 { 244 // Copy construction is defined as bit copy followed by postblit. 245 memcpy(p, value, element_size); 246 ti.postblit(p); 247 p += element_size; 248 } 249 } 250 catch (Throwable o) 251 { 252 // Destroy, in reverse order, what we've constructed so far 253 while (p > pstart) 254 { 255 p -= element_size; 256 ti.destroy(p); 257 } 258 259 throw o; 260 } 261 return pstart; 262 } 263