| /** | |
| * 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) ? malloc(elementSize) : buf.ptr; | |
| scope (exit) | |
| { | |
| if (ptmp != buf.ptr) | |
| free(ptmp); | |
| } | |
| 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 | |
| immutable maxAllocaSize = 512; | |
| void *ptmp = (element_size > maxAllocaSize) ? malloc(element_size) : alloca(element_size); | |
| foreach (i; 0 .. count) | |
| { | |
| memcpy(ptmp, p, element_size); | |
| memcpy(p, value, element_size); | |
| ti.postblit(p); | |
| ti.destroy(ptmp); | |
| p += element_size; | |
| } | |
| if (element_size > maxAllocaSize) | |
| free(ptmp); | |
| 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; | |
| } |