Skip to content

Commit e74dd80

Browse files
authored
Merge pull request #100 from n8sh/dynamicarray-putarray
Extend DynamicArray ~= to accept array of values merged-on-behalf-of: BBasile <BBasile@users.noreply.github.com>
2 parents 1387ae2 + b9f8783 commit e74dd80

File tree

1 file changed

+92
-12
lines changed

1 file changed

+92
-12
lines changed

src/containers/dynamicarray.d

Lines changed: 92 additions & 12 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.
@@ -124,7 +138,7 @@ struct DynamicArray(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange
124138
import std.traits: hasElaborateAssign, hasElaborateDestructor;
125139
static if (is(T == struct) && (hasElaborateAssign!T || hasElaborateDestructor!T))
126140
{
127-
// If a destructor is run before blit or copying involves
141+
// If a destructor is run before blit or assignment involves
128142
// more than just a blit, ensure that arr[l] is in a valid
129143
// state before assigning to it.
130144
import core.stdc.string : memcpy, memset;
@@ -143,34 +157,90 @@ struct DynamicArray(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange
143157
/**
144158
* ~= operator overload
145159
*/
146-
void opOpAssign(string op)(T value) if (op == "~")
160+
scope ref typeof(this) opOpAssign(string op)(T value) if (op == "~")
147161
{
148162
insert(value);
163+
return this;
164+
}
165+
166+
/**
167+
* ~= operator overload for an array of items
168+
*/
169+
scope ref typeof(this) opOpAssign(string op, bool checkForOverlap = true)(AppendT[] rhs)
170+
if (op == "~" && !is(T == AppendT[]))
171+
{
172+
// Disabling checkForOverlap when this function is called from opBinary!"~"
173+
// is not just for efficiency, but to avoid circular function calls that
174+
// would prevent inference of @nogc, etc.
175+
static if (checkForOverlap)
176+
if ((() @trusted => arr.ptr <= rhs.ptr && arr.ptr + arr.length > rhs.ptr)())
177+
{
178+
// Special case where rhs is a slice of this array.
179+
this = this ~ rhs;
180+
return this;
181+
}
182+
reserve(l + rhs.length);
183+
import std.traits: hasElaborateAssign, hasElaborateDestructor;
184+
static if (is(T == struct) && (hasElaborateAssign!T || hasElaborateDestructor!T))
185+
{
186+
// If a destructor is run before blit or assignment involves
187+
// more than just a blit, ensure that arr[l] is in a valid
188+
// state before assigning to it.
189+
import core.stdc.string : memcpy, memset;
190+
const init = typeid(T).initializer();
191+
if (init.ptr is null) // null pointer means initialize to 0s
192+
{
193+
foreach (ref value; rhs)
194+
{
195+
// We could call memset just once for the entire range
196+
// but this way has better memory locality.
197+
(() @trusted => memset(arr.ptr + l, 0, T.sizeof))();
198+
arr[l++] = value;
199+
}
200+
}
201+
else
202+
{
203+
foreach (ref value; rhs)
204+
{
205+
(() @trusted => memcpy(arr.ptr + l, init.ptr, T.sizeof))();
206+
arr[l++] = value;
207+
}
208+
}
209+
}
210+
else
211+
{
212+
arr[l .. l + rhs.length] = rhs[0 .. rhs.length];
213+
l += rhs.length;
214+
}
215+
return this;
216+
}
217+
218+
/// ditto
219+
scope ref typeof(this) opOpAssign(string op)(ref AppendTypeOfThis rhs)
220+
if (op == "~")
221+
{
222+
return this ~= rhs.arr[0 .. rhs.l];
149223
}
150224

151225
/**
152226
* ~ operator overload
153227
*/
154-
typeof(this) opBinary(string op)(ref typeof(this) other) if (op == "~")
228+
typeof(this) opBinary(string op)(ref AppendTypeOfThis other) if (op == "~")
155229
{
156230
typeof(this) ret;
157231
ret.reserve(l + other.l);
158-
foreach (value; arr[0 .. l])
159-
ret.insert(value);
160-
foreach (value; other.arr[0 .. other.l])
161-
ret.insert(value);
232+
ret.opOpAssign!("~", false)(arr[0 .. l]);
233+
ret.opOpAssign!("~", false)(other.arr[0 .. other.l]);
162234
return ret;
163235
}
164236

165237
/// ditto
166-
typeof(this) opBinary(string op)(T[] values) if (op == "~")
238+
typeof(this) opBinary(string op)(AppendT[] values) if (op == "~")
167239
{
168240
typeof(this) ret;
169241
ret.reserve(l + values.length);
170-
foreach (value; arr[0 .. l])
171-
ret.insert(value);
172-
foreach (value; values)
173-
ret.insert(value);
242+
ret.opOpAssign!("~", false)(arr[0 .. l]);
243+
ret.opOpAssign!("~", false)(values);
174244
return ret;
175245
}
176246

@@ -502,3 +572,13 @@ unittest
502572
auto hs = HStorage();
503573
}
504574

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

0 commit comments

Comments
 (0)