summaryrefslogtreecommitdiff
path: root/libphobos/libdruntime/core/internal/array/arrayassign.d
diff options
context:
space:
mode:
Diffstat (limited to 'libphobos/libdruntime/core/internal/array/arrayassign.d')
-rw-r--r--libphobos/libdruntime/core/internal/array/arrayassign.d304
1 files changed, 304 insertions, 0 deletions
diff --git a/libphobos/libdruntime/core/internal/array/arrayassign.d b/libphobos/libdruntime/core/internal/array/arrayassign.d
new file mode 100644
index 00000000000..6132e68db1a
--- /dev/null
+++ b/libphobos/libdruntime/core/internal/array/arrayassign.d
@@ -0,0 +1,304 @@
+module core.internal.array.arrayassign;
+
+// Force `enforceRawArraysConformable` to remain `pure` `@nogc`
+private void enforceRawArraysConformable(const char[] action, const size_t elementSize,
+ const void[] a1, const void[] a2, const bool allowOverlap) @trusted @nogc pure nothrow
+{
+ import core.internal.util.array : enforceRawArraysConformable;
+
+ alias Type = void function(const char[] action, const size_t elementSize,
+ const void[] a1, const void[] a2, in bool allowOverlap = false) @nogc pure nothrow;
+ (cast(Type)&enforceRawArraysConformable)(action, elementSize, a1, a2, allowOverlap);
+}
+
+private template CopyElem(string CopyAction)
+{
+ const char[] CopyElem = "{\n" ~ q{
+ memcpy(&tmp, cast(void*) &dst, elemSize);
+ } ~ CopyAction ~ q{
+ auto elem = cast(Unqual!T*) &tmp;
+ destroy(*elem);
+ } ~ "}\n";
+}
+
+private template CopyArray(bool CanOverlap, string CopyAction)
+{
+ const char[] CopyArray = CanOverlap ? q{
+ if (vFrom.ptr < vTo.ptr && vTo.ptr < vFrom.ptr + elemSize * vFrom.length)
+ foreach_reverse (i, ref dst; to)
+ } ~ CopyElem!(CopyAction) ~ q{
+ else
+ foreach (i, ref dst; to)
+ } ~ CopyElem!(CopyAction)
+ : q{
+ foreach (i, ref dst; to)
+ } ~ CopyElem!(CopyAction);
+}
+
+private template ArrayAssign(string CopyLogic, string AllowOverLap)
+{
+ const char[] ArrayAssign = q{
+ import core.internal.traits : hasElaborateCopyConstructor, Unqual;
+ import core.lifetime : copyEmplace;
+ import core.stdc.string : memcpy;
+
+ void[] vFrom = (cast(void*) from.ptr)[0 .. from.length];
+ void[] vTo = (cast(void*) to.ptr)[0 .. to.length];
+ enum elemSize = T.sizeof;
+
+ enforceRawArraysConformable("copy", elemSize, vFrom, vTo, } ~ AllowOverLap ~ q{);
+
+ void[elemSize] tmp = void;
+
+ } ~ CopyLogic ~ q{
+
+ return to;
+ };
+}
+
+/**
+ * Does array assignment (not construction) from another array of the same
+ * element type. Handles overlapping copies. Assumes the right hand side is an
+ * lvalue,
+ *
+ * Used for static array assignment with non-POD element types:
+ * ---
+ * struct S
+ * {
+ * ~this() {} // destructor, so not Plain Old Data
+ * }
+ *
+ * void main()
+ * {
+ * S[3] arr;
+ * S[3] lvalue;
+ *
+ * arr = lvalue;
+ * // Generates:
+ * // _d_arrayassign_l(arr[], lvalue[]), arr;
+ * }
+ * ---
+ *
+ * Params:
+ * to = destination array
+ * from = source array
+ * Returns:
+ * `to`
+ */
+Tarr _d_arrayassign_l(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted
+{
+ mixin(ArrayAssign!(q{
+ static if (hasElaborateCopyConstructor!T)
+ } ~ CopyArray!(true, "copyEmplace(from[i], dst);") ~ q{
+ else
+ } ~ CopyArray!(true, "memcpy(cast(void*) &dst, cast(void*) &from[i], elemSize);"),
+ "true"));
+}
+
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(int val) { this.val = val; }
+ this(const scope ref S rhs)
+ {
+ val = rhs.val;
+ counter++;
+ }
+ }
+
+ S[4] arr1;
+ S[4] arr2 = [S(0), S(1), S(2), S(3)];
+ _d_arrayassign_l(arr1[], arr2[]);
+
+ assert(counter == 4);
+ assert(arr1 == arr2);
+}
+
+// copy constructor
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(int val) { this.val = val; }
+ this(const scope ref S rhs)
+ {
+ val = rhs.val;
+ counter++;
+ }
+ }
+
+ S[4] arr1;
+ S[4] arr2 = [S(0), S(1), S(2), S(3)];
+ _d_arrayassign_l(arr1[], arr2[]);
+
+ assert(counter == 4);
+ assert(arr1 == arr2);
+}
+
+@safe nothrow unittest
+{
+ // Test that throwing works
+ int counter;
+ bool didThrow;
+
+ struct Throw
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ if (counter == 2)
+ throw new Exception("");
+ }
+ }
+ try
+ {
+ Throw[4] a;
+ Throw[4] b = [Throw(1), Throw(2), Throw(3), Throw(4)];
+ _d_arrayassign_l(a[], b[]);
+ }
+ catch (Exception)
+ {
+ didThrow = true;
+ }
+ assert(didThrow);
+ assert(counter == 2);
+
+
+ // Test that `nothrow` works
+ didThrow = false;
+ counter = 0;
+ struct NoThrow
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ }
+ }
+ try
+ {
+ NoThrow[4] a;
+ NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)];
+ _d_arrayassign_l(a[], b[]);
+ }
+ catch (Exception)
+ {
+ didThrow = false;
+ }
+ assert(!didThrow);
+ assert(counter == 4);
+}
+
+/**
+ * Does array assignment (not construction) from another array of the same
+ * element type. Does not support overlapping copies. Assumes the right hand
+ * side is an rvalue,
+ *
+ * Used for static array assignment with non-POD element types:
+ * ---
+ * struct S
+ * {
+ * ~this() {} // destructor, so not Plain Old Data
+ * }
+ *
+ * void main()
+ * {
+ * S[3] arr;
+ * S[3] getRvalue() {return lvalue;}
+ *
+ * arr = getRvalue();
+ * // Generates:
+ * // (__appendtmp = getRvalue), _d_arrayassign_l(arr[], __appendtmp), arr;
+ * }
+ * ---
+ *
+ * Params:
+ * to = destination array
+ * from = source array
+ * Returns:
+ * `to`
+ */
+Tarr _d_arrayassign_r(Tarr : T[], T)(return scope Tarr to, scope Tarr from) @trusted
+{
+ mixin(ArrayAssign!(
+ CopyArray!(false, "memcpy(cast(void*) &dst, cast(void*) &from[i], elemSize);"),
+ "false"));
+}
+
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(int val) { this.val = val; }
+ this(const scope ref S rhs)
+ {
+ val = rhs.val;
+ counter++;
+ }
+ }
+
+ S[4] arr1;
+ S[4] arr2 = [S(0), S(1), S(2), S(3)];
+ _d_arrayassign_r(arr1[], arr2[]);
+
+ assert(counter == 0);
+ assert(arr1 == arr2);
+}
+
+// copy constructor
+@safe unittest
+{
+ int counter;
+ struct S
+ {
+ int val;
+ this(int val) { this.val = val; }
+ this(const scope ref S rhs)
+ {
+ val = rhs.val;
+ counter++;
+ }
+ }
+
+ S[4] arr1;
+ S[4] arr2 = [S(0), S(1), S(2), S(3)];
+ _d_arrayassign_r(arr1[], arr2[]);
+
+ assert(counter == 0);
+ assert(arr1 == arr2);
+}
+
+@safe nothrow unittest
+{
+ // Test that `nothrow` works
+ bool didThrow = false;
+ int counter = 0;
+ struct NoThrow
+ {
+ int val;
+ this(this)
+ {
+ counter++;
+ }
+ }
+ try
+ {
+ NoThrow[4] a;
+ NoThrow[4] b = [NoThrow(1), NoThrow(2), NoThrow(3), NoThrow(4)];
+ _d_arrayassign_r(a[], b[]);
+ }
+ catch (Exception)
+ {
+ didThrow = false;
+ }
+ assert(!didThrow);
+ assert(counter == 0);
+}