Skip to content

Commit 9da0afa

Browse files
committed
Extend DynamicArray ~= to accept array of values
1 parent 04fe285 commit 9da0afa

File tree

1 file changed

+89
-11
lines changed

1 file changed

+89
-11
lines changed

src/containers/dynamicarray.d

Lines changed: 89 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ struct DynamicArray(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange
2626

2727
private import stdx.allocator.common : stateSize;
2828

29+
static if (is(typeof((T[] a, const T[] b) => a[0 .. b.length] = b[0 .. $])))
30+
{
31+
/// Either const(T) or T.
32+
alias AppendT = const(T);
33+
34+
/// Either const(typeof(this)) or typeof(this).
35+
alias AppendTypeOfThis = const(typeof(this));
36+
}
37+
else
38+
{
39+
alias AppendT = T;
40+
alias AppendTypeOfThis = typeof(this);
41+
}
42+
2943
static if (stateSize!Allocator != 0)
3044
{
3145
/// No default construction if an allocator must be provided.
@@ -123,7 +137,7 @@ struct DynamicArray(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange
123137
import std.traits: hasElaborateAssign, hasElaborateDestructor;
124138
static if (is(T == struct) && (hasElaborateAssign!T || hasElaborateDestructor!T))
125139
{
126-
// If a destructor is run before blit or copying involves
140+
// If a destructor is run before blit or assignment involves
127141
// more than just a blit, ensure that arr[l] is in a valid
128142
// state before assigning to it.
129143
import core.stdc.string : memcpy, memset;
@@ -147,29 +161,83 @@ struct DynamicArray(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange
147161
insert(value);
148162
}
149163

164+
/**
165+
* ~= operator overload for an array of items
166+
*/
167+
void opOpAssign(string op, bool checkForOverlap = true)(AppendT[] rhs)
168+
if (op == "~" && !is(T == AppendT[]))
169+
{
170+
// Disabling checkForOverlap when this function is called from opBinary!"~"
171+
// is not just for efficiency, but to avoid circular function calls that
172+
// would prevent inference of @nogc, etc.
173+
static if (checkForOverlap)
174+
if ((() @trusted => arr.ptr <= rhs.ptr && arr.ptr + arr.length > rhs.ptr)())
175+
{
176+
// Special case where rhs is a slice of this array.
177+
this = this ~ rhs;
178+
return;
179+
}
180+
reserve(l + rhs.length);
181+
import std.traits: hasElaborateAssign, hasElaborateDestructor;
182+
static if (is(T == struct) && (hasElaborateAssign!T || hasElaborateDestructor!T))
183+
{
184+
// If a destructor is run before blit or assignment involves
185+
// more than just a blit, ensure that arr[l] is in a valid
186+
// state before assigning to it.
187+
import core.stdc.string : memcpy, memset;
188+
const init = typeid(T).initializer();
189+
if (init.ptr is null) // null pointer means initialize to 0s
190+
{
191+
foreach (ref value; rhs)
192+
{
193+
// We could call memset just once for the entire range
194+
// but this way has better memory locality.
195+
(() @trusted => memset(arr.ptr + l, 0, T.sizeof))();
196+
arr[l++] = value;
197+
}
198+
}
199+
else
200+
{
201+
foreach (ref value; rhs)
202+
{
203+
(() @trusted => memcpy(arr.ptr + l, init.ptr, T.sizeof))();
204+
arr[l++] = value;
205+
}
206+
}
207+
}
208+
else
209+
{
210+
arr[l .. l + rhs.length] = rhs[0 .. rhs.length];
211+
l += rhs.length;
212+
}
213+
}
214+
215+
/// ditto
216+
void opOpAssign(string op)(ref AppendTypeOfThis rhs)
217+
if (op == "~")
218+
{
219+
this ~= rhs.arr[0 .. rhs.l];
220+
}
221+
150222
/**
151223
* ~ operator overload
152224
*/
153-
typeof(this) opBinary(string op)(ref typeof(this) other) if (op == "~")
225+
typeof(this) opBinary(string op)(ref AppendTypeOfThis other) if (op == "~")
154226
{
155227
typeof(this) ret;
156228
ret.reserve(l + other.l);
157-
foreach (value; arr[0 .. l])
158-
ret.insert(value);
159-
foreach (value; other.arr[0 .. other.l])
160-
ret.insert(value);
229+
ret.opOpAssign!("~", false)(arr[0 .. l]);
230+
ret.opOpAssign!("~", false)(other.arr[0 .. other.l]);
161231
return ret;
162232
}
163233

164234
/// ditto
165-
typeof(this) opBinary(string op)(T[] values) if (op == "~")
235+
typeof(this) opBinary(string op)(AppendT[] values) if (op == "~")
166236
{
167237
typeof(this) ret;
168238
ret.reserve(l + values.length);
169-
foreach (value; arr[0 .. l])
170-
ret.insert(value);
171-
foreach (value; values)
172-
ret.insert(value);
239+
ret.opOpAssign!("~", false)(arr[0 .. l]);
240+
ret.opOpAssign!("~", false)(values);
173241
return ret;
174242
}
175243

@@ -501,3 +569,13 @@ unittest
501569
auto hs = HStorage();
502570
}
503571

572+
@nogc unittest
573+
{
574+
DynamicArray!char a;
575+
const DynamicArray!char b = a ~ "def";
576+
a ~= "abc";
577+
a ~= b;
578+
assert(a[] == "abcdef");
579+
a ~= a;
580+
assert(a[] == "abcdefabcdef");
581+
}

0 commit comments

Comments
 (0)