/** * Implementation of array assignment support routines. * * * Copyright: Copyright Digital Mars 2010 - 2016. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * Authors: Walter Bright, Kenji Hara * Source: $(DRUNTIMESRC src/rt/_arrayassign.d) */ module rt.arrayassign; private { import rt.util.array; import core.stdc.string; import core.stdc.stdlib; debug(PRINTF) import core.stdc.stdio; } /** * Keep for backward binary compatibility. This function can be removed in the future. */ extern (C) void[] _d_arrayassign(TypeInfo ti, void[] from, void[] to) { debug(PRINTF) printf("_d_arrayassign(from = %p,%d, to = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, ti.tsize); immutable elementSize = ti.tsize; // Need a temporary buffer tmp[] big enough to hold one element void[16] buf = void; void* ptmp = (elementSize > buf.sizeof) ? alloca(elementSize) : buf.ptr; return _d_arrayassign_l(ti, from, to, ptmp); } /** * Does array assignment (not construction) from another * lvalue array of the same element type. * Handles overlapping copies. * Input: * ti TypeInfo of the element type. * dst Points target memory. Its .length is equal to the element count, not byte length. * src Points source memory. Its .length is equal to the element count, not byte length. * ptmp Temporary memory for element swapping. */ extern (C) void[] _d_arrayassign_l(TypeInfo ti, void[] src, void[] dst, void* ptmp) { 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); immutable elementSize = ti.tsize; enforceRawArraysConformable("copy", elementSize, src, dst, true); if (src.ptr < dst.ptr && dst.ptr < src.ptr + elementSize * src.length) { // If dst is in the middle of src memory, use reverse order. for (auto i = dst.length; i--; ) { void* pdst = dst.ptr + i * elementSize; void* psrc = src.ptr + i * elementSize; memcpy(ptmp, pdst, elementSize); memcpy(pdst, psrc, elementSize); ti.postblit(pdst); ti.destroy(ptmp); } } else { // Otherwise, use normal order. foreach (i; 0 .. dst.length) { void* pdst = dst.ptr + i * elementSize; void* psrc = src.ptr + i * elementSize; memcpy(ptmp, pdst, elementSize); memcpy(pdst, psrc, elementSize); ti.postblit(pdst); ti.destroy(ptmp); } } return dst; } unittest // Bugzilla 14024 { string op; struct S { char x = 'x'; this(this) { op ~= x-0x20; } // upper case ~this() { op ~= x; } // lower case } S[4] mem; ref S[2] slice(int a, int b) { return mem[a .. b][0 .. 2]; } op = null; mem[0].x = 'a'; mem[1].x = 'b'; mem[2].x = 'x'; mem[3].x = 'y'; slice(0, 2) = slice(2, 4); // [ab] = [xy] assert(op == "XaYb", op); op = null; mem[0].x = 'x'; mem[1].x = 'y'; mem[2].x = 'a'; mem[3].x = 'b'; slice(2, 4) = slice(0, 2); // [ab] = [xy] assert(op == "XaYb", op); op = null; mem[0].x = 'a'; mem[1].x = 'b'; mem[2].x = 'c'; slice(0, 2) = slice(1, 3); // [ab] = [bc] assert(op == "BaCb", op); op = null; mem[0].x = 'x'; mem[1].x = 'y'; mem[2].x = 'z'; slice(1, 3) = slice(0, 2); // [yz] = [xy] assert(op == "YzXy", op); } /** * Does array assignment (not construction) from another * rvalue array of the same element type. * Input: * ti TypeInfo of the element type. * dst Points target memory. Its .length is equal to the element count, not byte length. * src Points source memory. Its .length is equal to the element count, not byte length. * It is always allocated on stack and never overlapping with dst. * ptmp Temporary memory for element swapping. */ extern (C) void[] _d_arrayassign_r(TypeInfo ti, void[] src, void[] dst, void* ptmp) { 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); immutable elementSize = ti.tsize; enforceRawArraysConformable("copy", elementSize, src, dst, false); // Always use normal order, because we can assume that // the rvalue src has no overlapping with dst. foreach (i; 0 .. dst.length) { void* pdst = dst.ptr + i * elementSize; void* psrc = src.ptr + i * elementSize; memcpy(ptmp, pdst, elementSize); memcpy(pdst, psrc, elementSize); ti.destroy(ptmp); } return dst; } /** * Does array initialization (not assignment) from another * array of the same element type. * ti is the element type. */ extern (C) void[] _d_arrayctor(TypeInfo ti, void[] from, void[] to) { debug(PRINTF) printf("_d_arrayctor(from = %p,%d, to = %p,%d) size = %d\n", from.ptr, from.length, to.ptr, to.length, ti.tsize); auto element_size = ti.tsize; enforceRawArraysConformable("initialization", element_size, from, to); size_t i; try { for (i = 0; i < to.length; i++) { // Copy construction is defined as bit copy followed by postblit. memcpy(to.ptr + i * element_size, from.ptr + i * element_size, element_size); ti.postblit(to.ptr + i * element_size); } } catch (Throwable o) { /* Destroy, in reverse order, what we've constructed so far */ while (i--) { ti.destroy(to.ptr + i * element_size); } throw o; } return to; } /** * Do assignment to an array. * p[0 .. count] = value; */ extern (C) void* _d_arraysetassign(void* p, void* value, int count, TypeInfo ti) { void* pstart = p; auto element_size = ti.tsize; //Need a temporary buffer tmp[] big enough to hold one element void[16] buf = void; void[] tmp; if (element_size > buf.sizeof) { tmp = alloca(element_size)[0 .. element_size]; } else tmp = buf[]; foreach (i; 0 .. count) { memcpy(tmp.ptr, p, element_size); memcpy(p, value, element_size); ti.postblit(p); ti.destroy(tmp.ptr); p += element_size; } return pstart; } /** * Do construction of an array. * ti[count] p = value; */ extern (C) void* _d_arraysetctor(void* p, void* value, int count, TypeInfo ti) { void* pstart = p; auto element_size = ti.tsize; try { foreach (i; 0 .. count) { // Copy construction is defined as bit copy followed by postblit. memcpy(p, value, element_size); ti.postblit(p); p += element_size; } } catch (Throwable o) { // Destroy, in reverse order, what we've constructed so far while (p > pstart) { p -= element_size; ti.destroy(p); } throw o; } return pstart; }