blob: 389ff92ef786709280df84e98ba0ad21b01693f9 [file] [log] [blame]
/**
* 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;
}