mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 22:50:38 +03:00

With this fix, Appender and builtin appending should both be callable on the same data without corruption, of course using the builtin append on data held by an Appender will reallocate.
1159 lines
27 KiB
D
1159 lines
27 KiB
D
// Written in the D programming language.
|
|
|
|
/**
|
|
Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
|
|
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
|
|
Authors: $(WEB erdani.org, Andrei Alexandrescu)
|
|
|
|
Copyright Andrei Alexandrescu 2008 - 2009.
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file LICENSE_1_0.txt or copy at
|
|
http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
module std.array;
|
|
|
|
import std.c.stdio;
|
|
import core.memory;
|
|
import std.algorithm, std.conv, std.encoding, std.exception, std.range,
|
|
std.string, std.traits, std.typecons, std.utf;
|
|
private import std.c.string : memcpy;
|
|
private import std.intrinsic : bsr;
|
|
version(unittest) private import std.stdio;
|
|
|
|
/**
|
|
Returns a newly-allocated dynamic array consisting of a copy of the input
|
|
range, static array, dynamic array, or class or struct with an $(D opApply)
|
|
function $(D r). Note that narrow strings are handled
|
|
as a special case in an overload.
|
|
|
|
Example:
|
|
|
|
----
|
|
auto a = array([1, 2, 3, 4, 5][]);
|
|
assert(a == [ 1, 2, 3, 4, 5 ]);
|
|
----
|
|
*/
|
|
ForeachType!Range[] array(Range)(Range r)
|
|
if (isIterable!Range && !isNarrowString!Range)
|
|
{
|
|
alias ForeachType!Range E;
|
|
static if (hasLength!Range)
|
|
{
|
|
if (r.empty) return null;
|
|
|
|
// Determines whether the GC should scan the array.
|
|
auto blkInfo = (typeid(E).flags & 1) ?
|
|
cast(GC.BlkAttr) 0 :
|
|
GC.BlkAttr.NO_SCAN;
|
|
|
|
auto result = (cast(E*) enforce(GC.malloc(r.length * E.sizeof, blkInfo),
|
|
text("Out of memory while allocating an array of ", r.length,
|
|
" objects of type ", E.stringof)))[0 .. r.length];
|
|
size_t i = 0;
|
|
foreach (e; r)
|
|
{
|
|
// hacky
|
|
static if (is(typeof(e.opAssign(e))))
|
|
{
|
|
// this should be in-place construction
|
|
auto voidArr = (cast(void*) (result.ptr + i))[0..E.sizeof];
|
|
emplace!E(voidArr, e);
|
|
}
|
|
else
|
|
{
|
|
result[i] = e;
|
|
}
|
|
i++;
|
|
}
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
auto a = appender!(E[])();
|
|
foreach (e; r)
|
|
{
|
|
a.put(e);
|
|
}
|
|
return a.data;
|
|
}
|
|
// // 2. Initialize the memory
|
|
// size_t constructedElements = 0;
|
|
// scope(failure)
|
|
// {
|
|
// // Deconstruct only what was constructed
|
|
// foreach_reverse (i; 0 .. constructedElements)
|
|
// {
|
|
// try
|
|
// {
|
|
// //result[i].~E();
|
|
// }
|
|
// catch (Exception e)
|
|
// {
|
|
// }
|
|
// }
|
|
// // free the entire array
|
|
// std.gc.realloc(result, 0);
|
|
// }
|
|
// foreach (src; elements)
|
|
// {
|
|
// static if (is(typeof(new(result + constructedElements) E(src))))
|
|
// {
|
|
// new(result + constructedElements) E(src);
|
|
// }
|
|
// else
|
|
// {
|
|
// result[constructedElements] = src;
|
|
// }
|
|
// ++constructedElements;
|
|
// }
|
|
// // 3. Success constructing all elements, type the array and return it
|
|
// setTypeInfo(typeid(E), result);
|
|
// return result[0 .. constructedElements];
|
|
}
|
|
|
|
/**
|
|
Convert a narrow string to an array type that fully supports random access.
|
|
This is handled as a special case and always returns a $(D dchar[]),
|
|
$(D const(dchar)[]), or $(D immutable(dchar)[]) depending on the constness of
|
|
the input.
|
|
*/
|
|
ElementType!String[] array(String)(String str) if(isNarrowString!String)
|
|
{
|
|
static if(is(typeof(return) == immutable))
|
|
{
|
|
return to!(immutable(dchar)[])(str);
|
|
}
|
|
else static if(is(typeof(return) == const))
|
|
{
|
|
return to!(const(dchar)[])(str);
|
|
}
|
|
else
|
|
{
|
|
return to!(dchar[])(str);
|
|
}
|
|
}
|
|
|
|
version(unittest)
|
|
{
|
|
struct TestArray { int x; string toString() { return .to!string(x); } }
|
|
|
|
struct OpAssign
|
|
{
|
|
uint num;
|
|
this(uint num) { this.num = num; }
|
|
|
|
// Templating opAssign to make sure the bugs with opAssign being
|
|
// templated are fixed.
|
|
void opAssign(T)(T rhs) { this.num = rhs.num; }
|
|
}
|
|
|
|
struct OpApply
|
|
{
|
|
int opApply(int delegate(ref int) dg)
|
|
{
|
|
int res;
|
|
foreach(i; 0..10)
|
|
{
|
|
res = dg(i);
|
|
if(res) break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto a = array([1, 2, 3, 4, 5][]);
|
|
//writeln(a);
|
|
assert(a == [ 1, 2, 3, 4, 5 ]);
|
|
|
|
auto b = array([TestArray(1), TestArray(2)][]);
|
|
//writeln(b);
|
|
|
|
class C
|
|
{
|
|
int x;
|
|
this(int y) { x = y; }
|
|
override string toString() { return .to!string(x); }
|
|
}
|
|
auto c = array([new C(1), new C(2)][]);
|
|
//writeln(c);
|
|
|
|
auto d = array([1., 2.2, 3][]);
|
|
assert(is(typeof(d) == double[]));
|
|
//writeln(d);
|
|
|
|
auto e = [OpAssign(1), OpAssign(2)];
|
|
auto f = array(e);
|
|
assert(e == f);
|
|
|
|
assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
|
|
assert(array("ABC") == "ABC"d);
|
|
assert(array("ABC".dup) == "ABC"d.dup);
|
|
}
|
|
|
|
template IndexType(C : T[], T)
|
|
{
|
|
alias size_t IndexType;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
static assert(is(IndexType!(double[]) == size_t));
|
|
static assert(!is(IndexType!(double) == size_t));
|
|
}
|
|
|
|
/**
|
|
Implements the range interface primitive $(D empty) for built-in
|
|
arrays. Due to the fact that nonmember functions can be called with
|
|
the first argument using the dot notation, $(D array.empty) is
|
|
equivalent to $(D empty(array)).
|
|
|
|
Example:
|
|
----
|
|
void main()
|
|
{
|
|
auto a = [ 1, 2, 3 ];
|
|
assert(!a.empty);
|
|
assert(a[3 .. $].empty);
|
|
}
|
|
----
|
|
*/
|
|
|
|
@property bool empty(T)(in T[] a) { return !a.length; }
|
|
|
|
unittest
|
|
{
|
|
auto a = [ 1, 2, 3 ];
|
|
assert(!a.empty);
|
|
assert(a[3 .. $].empty);
|
|
}
|
|
|
|
/**
|
|
Implements the range interface primitive $(D save) for built-in
|
|
arrays. Due to the fact that nonmember functions can be called with
|
|
the first argument using the dot notation, $(D array.save) is
|
|
equivalent to $(D save(array)).
|
|
|
|
Example:
|
|
----
|
|
void main()
|
|
{
|
|
auto a = [ 1, 2, 3 ];
|
|
auto b = a.save;
|
|
assert(b is a);
|
|
}
|
|
----
|
|
*/
|
|
|
|
@property T[] save(T)(T[] a)
|
|
{
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
Implements the range interface primitive $(D popFront) for built-in
|
|
arrays. Due to the fact that nonmember functions can be called with
|
|
the first argument using the dot notation, $(D array.popFront) is
|
|
equivalent to $(D popFront(array)).
|
|
|
|
|
|
Example:
|
|
----
|
|
void main()
|
|
{
|
|
int[] a = [ 1, 2, 3 ];
|
|
a.popFront;
|
|
assert(a == [ 2, 3 ]);
|
|
}
|
|
----
|
|
*/
|
|
|
|
void popFront(T)(ref T[] a) if (!is(Unqual!T == char) && !is(Unqual!T == wchar))
|
|
{
|
|
assert(a.length, "Attempting to popFront() past the end of an array of "
|
|
~ T.stringof);
|
|
a = a[1 .. $];
|
|
}
|
|
|
|
unittest
|
|
{
|
|
//@@@BUG 2608@@@
|
|
//auto a = [ 1, 2, 3 ];
|
|
int[] a = [ 1, 2, 3 ];
|
|
a.popFront;
|
|
assert(a == [ 2, 3 ]);
|
|
}
|
|
|
|
void popFront(T)(ref T[] a) if (is(Unqual!T == char) || is(Unqual!T == wchar))
|
|
{
|
|
assert(a.length, "Attempting to popFront() past the end of an array of "
|
|
~ T.stringof);
|
|
a = a[std.utf.stride(a, 0) .. $];
|
|
}
|
|
|
|
unittest
|
|
{
|
|
string s1 = "\xC2\xA9hello";
|
|
s1.popFront();
|
|
assert(s1 == "hello");
|
|
wstring s2 = "\xC2\xA9hello";
|
|
s2.popFront();
|
|
assert(s2 == "hello");
|
|
string s3 = "\u20AC100";
|
|
//write(s3, '\n');
|
|
}
|
|
|
|
/**
|
|
Implements the range interface primitive $(D popBack) for built-in
|
|
arrays. Due to the fact that nonmember functions can be called with
|
|
the first argument using the dot notation, $(D array.popBack) is
|
|
equivalent to $(D popBack(array)).
|
|
|
|
|
|
Example:
|
|
----
|
|
void main()
|
|
{
|
|
int[] a = [ 1, 2, 3 ];
|
|
a.popBack;
|
|
assert(a == [ 1, 2 ]);
|
|
}
|
|
----
|
|
*/
|
|
|
|
void popBack(T)(ref T[] a) if (!is(Unqual!T == char) && !is(Unqual!T == wchar))
|
|
{
|
|
assert(a.length);
|
|
a = a[0 .. $ - 1];
|
|
}
|
|
|
|
unittest
|
|
{
|
|
//@@@BUG 2608@@@
|
|
//auto a = [ 1, 2, 3 ];
|
|
int[] a = [ 1, 2, 3 ];
|
|
a.popBack;
|
|
assert(a == [ 1, 2 ]);
|
|
}
|
|
|
|
void popBack(T)(ref T[] a) if (is(Unqual!T == char))
|
|
{
|
|
immutable n = a.length;
|
|
const p = a.ptr + n;
|
|
if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000)
|
|
{
|
|
a = a[0 .. n - 1];
|
|
}
|
|
else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000)
|
|
{
|
|
a = a[0 .. n - 2];
|
|
}
|
|
else if (n >= 3 && (p[-3] & 0b1100_0000) != 0b1000_0000)
|
|
{
|
|
a = a[0 .. n - 3];
|
|
}
|
|
else if (n >= 4 && (p[-4] & 0b1100_0000) != 0b1000_0000)
|
|
{
|
|
a = a[0 .. n - 4];
|
|
}
|
|
else
|
|
{
|
|
assert(false, "Invalid UTF character at end of string");
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
string s = "hello\xE2\x89\xA0";
|
|
s.popBack();
|
|
assert(s == "hello", s);
|
|
|
|
string s3 = "\xE2\x89\xA0";
|
|
auto c = s3.back;
|
|
assert(c == cast(dchar)'\u2260');
|
|
s3.popBack();
|
|
assert(s3 == "");
|
|
}
|
|
|
|
void popBack(T)(ref T[] a) if (is(Unqual!T == wchar))
|
|
{
|
|
assert(a.length);
|
|
if (a.length == 1)
|
|
{
|
|
a = a[0 .. 0];
|
|
return;
|
|
}
|
|
immutable c = a[$ - 2];
|
|
a = a[0 .. $ - 1 - (c >= 0xD800 && c <= 0xDBFF)];
|
|
}
|
|
|
|
unittest
|
|
{
|
|
wstring s = "hello\xE2\x89\xA0";
|
|
s.popBack();
|
|
assert(s == "hello");
|
|
}
|
|
|
|
/**
|
|
Implements the range interface primitive $(D front) for built-in
|
|
arrays. Due to the fact that nonmember functions can be called with
|
|
the first argument using the dot notation, $(D array.front) is
|
|
equivalent to $(D front(array)).
|
|
|
|
|
|
Example:
|
|
----
|
|
void main()
|
|
{
|
|
int[] a = [ 1, 2, 3 ];
|
|
assert(a.front == 1);
|
|
}
|
|
----
|
|
*/
|
|
ref typeof(A[0]) front(A)(A a) if (is(typeof(A[0])) && !isNarrowString!A)
|
|
{
|
|
assert(a.length, "Attempting to fetch the front of an empty array");
|
|
return a[0];
|
|
}
|
|
|
|
dchar front(A)(A a) if (is(typeof(A[0])) && isNarrowString!A)
|
|
{
|
|
assert(a.length, "Attempting to fetch the front of an empty array");
|
|
size_t i = 0;
|
|
return decode(a, i);
|
|
}
|
|
|
|
/// Ditto
|
|
void front(T)(T[] a, T v) if (!isNarrowString!A)
|
|
{
|
|
assert(a.length); a[0] = v;
|
|
}
|
|
|
|
/**
|
|
Implements the range interface primitive $(D back) for built-in
|
|
arrays. Due to the fact that nonmember functions can be called with
|
|
the first argument using the dot notation, $(D array.back) is
|
|
equivalent to $(D back(array)).
|
|
|
|
Example:
|
|
----
|
|
void main()
|
|
{
|
|
int[] a = [ 1, 2, 3 ];
|
|
assert(a.back == 3);
|
|
}
|
|
----
|
|
*/
|
|
ref typeof(A.init[0]) back(A)(A a)
|
|
if (is(typeof(A.init[0])) && !isNarrowString!A)
|
|
{
|
|
// @@@BUG@@@ The assert below crashes the unittest due to a bug in
|
|
// the compiler
|
|
version (bug4426)
|
|
{
|
|
assert(a.length, "Attempting to fetch the back of an empty array");
|
|
}
|
|
else
|
|
{
|
|
assert(a.length);
|
|
}
|
|
return a[$ - 1];
|
|
}
|
|
|
|
unittest
|
|
{
|
|
int[] a = [ 1, 2, 3 ];
|
|
assert(a.back == 3);
|
|
a.back += 4;
|
|
assert(a.back == 7);
|
|
}
|
|
|
|
dchar back(A)(A a)
|
|
if (is(typeof(A.init[0])) && isNarrowString!A && a[0].sizeof < 4)
|
|
{
|
|
assert(a.length, "Attempting to fetch the back of an empty array");
|
|
auto n = a.length;
|
|
const p = a.ptr + n;
|
|
if (n >= 1 && (p[-1] & 0b1100_0000) != 0b1000_0000)
|
|
{
|
|
--n;
|
|
return std.utf.decode(a, n);
|
|
}
|
|
else if (n >= 2 && (p[-2] & 0b1100_0000) != 0b1000_0000)
|
|
{
|
|
n -= 2;
|
|
return decode(a, n);
|
|
}
|
|
else if (n >= 3 && (p[-3] & 0b1100_0000) != 0b1000_0000)
|
|
{
|
|
n -= 3;
|
|
return decode(a, n);
|
|
}
|
|
else if (n >= 4 && (p[-4] & 0b1100_0000) != 0b1000_0000)
|
|
{
|
|
n -= 4;
|
|
return decode(a, n);
|
|
}
|
|
else
|
|
{
|
|
throw new UtfException("Invalid UTF character at end of string");
|
|
}
|
|
}
|
|
|
|
// overlap
|
|
/*
|
|
Returns the overlapping portion, if any, of two arrays. Unlike $(D
|
|
equal), $(D overlap) only compares the pointers in the ranges, not the
|
|
values referred by them. If $(D r1) and $(D r2) have an overlapping
|
|
slice, returns that slice. Otherwise, returns the null slice.
|
|
|
|
Example:
|
|
----
|
|
int[] a = [ 10, 11, 12, 13, 14 ];
|
|
int[] b = a[1 .. 3];
|
|
assert(overlap(a, b) == [ 11, 12 ]);
|
|
b = b.dup;
|
|
// overlap disappears even though the content is the same
|
|
assert(isEmpty(overlap(a, b)));
|
|
----
|
|
*/
|
|
T[] overlap(T)(T[] r1, T[] r2)
|
|
{
|
|
auto b = max(r1.ptr, r2.ptr);
|
|
auto e = min(&(r1.ptr[r1.length - 1]) + 1, &(r2.ptr[r2.length - 1]) + 1);
|
|
return b < e ? b[0 .. e - b] : null;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
int[] a = [ 10, 11, 12, 13, 14 ];
|
|
int[] b = a[1 .. 3];
|
|
a[1] = 100;
|
|
assert(overlap(a, b) == [ 100, 12 ]);
|
|
}
|
|
|
|
/**
|
|
Inserts $(D stuff) in $(D container) at position $(D pos).
|
|
*/
|
|
void insert(T, Range)(ref T[] array, size_t pos, Range stuff)
|
|
{
|
|
static if (is(typeof(stuff[0])))
|
|
{
|
|
// presumably an array
|
|
alias stuff toInsert;
|
|
//assert(!overlap(array, toInsert));
|
|
}
|
|
else
|
|
{
|
|
// presumably only one element
|
|
auto toInsert = (&stuff)[0 .. 1];
|
|
}
|
|
|
|
// @@@BUG 2130@@@
|
|
// immutable
|
|
// size_t delta = toInsert.length,
|
|
// size_t oldLength = array.length,
|
|
// size_t newLength = oldLength + delta;
|
|
immutable
|
|
delta = toInsert.length,
|
|
oldLength = array.length,
|
|
newLength = oldLength + delta;
|
|
|
|
// Reallocate the array to make space for new content
|
|
array = (cast(T*) core.memory.GC.realloc(array.ptr,
|
|
newLength * array[0].sizeof))[0 .. newLength];
|
|
assert(array.length == newLength);
|
|
|
|
// Move data in pos .. pos + stuff.length to the end of the array
|
|
foreach_reverse (i; pos .. oldLength)
|
|
{
|
|
// This will be guaranteed to not throw
|
|
move(array[i], array[i + delta]);
|
|
}
|
|
|
|
// Copy stuff into array
|
|
foreach (e; toInsert)
|
|
{
|
|
array[pos++] = e;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
int[] a = ([1, 4, 5]).dup;
|
|
insert(a, 1u, [2, 3]);
|
|
assert(a == [1, 2, 3, 4, 5]);
|
|
insert(a, 1u, 99);
|
|
assert(a == [1, 99, 2, 3, 4, 5]);
|
|
}
|
|
|
|
// @@@ TODO: document this
|
|
bool sameHead(T)(in T[] lhs, in T[] rhs)
|
|
{
|
|
return lhs.ptr == rhs.ptr;
|
|
}
|
|
|
|
/**
|
|
Erases elements from $(D array) with indices ranging from $(D from)
|
|
(inclusive) to $(D to) (exclusive).
|
|
*/
|
|
// void erase(T)(ref T[] array, size_t from, size_t to)
|
|
// {
|
|
// immutable newLength = array.length - (to - from);
|
|
// foreach (i; to .. array.length)
|
|
// {
|
|
// move(array[i], array[from++]);
|
|
// }
|
|
// array.length = newLength;
|
|
// }
|
|
|
|
// unittest
|
|
// {
|
|
// int[] a = [1, 2, 3, 4, 5];
|
|
// erase(a, 1u, 3u);
|
|
// assert(a == [1, 4, 5]);
|
|
// }
|
|
|
|
/**
|
|
Erases element from $(D array) at index $(D from).
|
|
*/
|
|
// void erase(T)(ref T[] array, size_t from)
|
|
// {
|
|
// erase(array, from, from + 1);
|
|
// }
|
|
|
|
// unittest
|
|
// {
|
|
// int[] a = [1, 2, 3, 4, 5];
|
|
// erase(a, 2u);
|
|
// assert(a == [1, 2, 4, 5]);
|
|
// }
|
|
|
|
/**
|
|
Replaces elements from $(D array) with indices ranging from $(D from)
|
|
(inclusive) to $(D to) (exclusive) with the range $(D stuff). Expands
|
|
or shrinks the array as needed.
|
|
*/
|
|
void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
|
|
if (is(ElementType!Range == T))
|
|
{
|
|
// container = container[0 .. from] ~ stuff ~ container[to .. $];
|
|
if (overlap(array, stuff))
|
|
{
|
|
// use slower/conservative method
|
|
array = array[0 .. from] ~ stuff ~ array[to .. $];
|
|
}
|
|
else if (stuff.length <= to - from)
|
|
{
|
|
// replacement reduces length
|
|
// BUG 2128
|
|
//immutable stuffEnd = from + stuff.length;
|
|
auto stuffEnd = from + stuff.length;
|
|
array[from .. stuffEnd] = stuff;
|
|
array = remove(array, tuple(stuffEnd, to));
|
|
}
|
|
else
|
|
{
|
|
// replacement increases length
|
|
// @@@TODO@@@: optimize this
|
|
immutable replaceLen = to - from;
|
|
array[from .. to] = stuff[0 .. replaceLen];
|
|
insert(array, to, stuff[replaceLen .. $]);
|
|
}
|
|
}
|
|
|
|
|
|
void replace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff)
|
|
if (!is(ElementType!Range == T) && is(Unqual!Range == void*))
|
|
{
|
|
replace(array, from, to, cast(T[])[]);
|
|
}
|
|
|
|
|
|
|
|
unittest
|
|
{
|
|
int[] a = [1, 4, 5];
|
|
replace(a, 1u, 2u, [2, 3, 4]);
|
|
assert(a == [1, 2, 3, 4, 5]);
|
|
replace(a, 1u, 2u, cast(int[])[]);
|
|
assert(a == [1, 3, 4, 5]);
|
|
replace(a, 1u, 2u, null);
|
|
assert(a == [1, 4, 5]);
|
|
}
|
|
|
|
/**
|
|
Implements an output range that appends data to an array. This is
|
|
recommended over $(D a ~= data) when appending many elements because it is more
|
|
efficient.
|
|
|
|
Example:
|
|
----
|
|
auto app = appender!string();
|
|
string b = "abcdefg";
|
|
foreach (char c; b) app.put(c);
|
|
assert(app.data == "abcdefg");
|
|
|
|
int[] a = [ 1, 2 ];
|
|
auto app2 = appender(a);
|
|
app2.put(3);
|
|
app2.put([ 4, 5, 6 ]);
|
|
assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]);
|
|
----
|
|
*/
|
|
|
|
struct Appender(A : T[], T)
|
|
{
|
|
private struct Data
|
|
{
|
|
size_t capacity;
|
|
Unqual!(T)[] arr;
|
|
}
|
|
|
|
private Data* _data;
|
|
|
|
/**
|
|
Construct an appender with a given array. Note that this does not copy the
|
|
data. If the array has a larger capacity as determined by arr.capacity,
|
|
it will be used by the appender. After initializing an appender on an array,
|
|
appending to the original array will reallocate.
|
|
*/
|
|
this(T[] arr)
|
|
{
|
|
// initialize to a given array.
|
|
_data = new Data;
|
|
_data.arr = cast(Unqual!(T)[])arr;
|
|
|
|
// We want to use up as much of the block the array is in as possible.
|
|
// if we consume all the block that we can, then array appending is
|
|
// safe WRT built-in append, and we can use the entire block.
|
|
auto cap = arr.capacity;
|
|
if(cap > arr.length)
|
|
arr.length = cap;
|
|
// we assume no reallocation occurred
|
|
assert(arr.ptr is _data.arr.ptr);
|
|
_data.capacity = arr.length;
|
|
}
|
|
|
|
/**
|
|
Reserve at least newCapacity elements for appending. Note that more elements
|
|
may be reserved than requested. If newCapacity < capacity, then nothing is
|
|
done.
|
|
*/
|
|
void reserve(size_t newCapacity)
|
|
{
|
|
if(!_data)
|
|
_data = new Data;
|
|
if(_data.capacity < newCapacity)
|
|
{
|
|
// need to increase capacity
|
|
auto bi = GC.qalloc(newCapacity * T.sizeof, (typeid(T[]).next.flags & 1) ? GC.BlkAttr.NO_SCAN : 0);
|
|
_data.capacity = bi.size / T.sizeof;
|
|
if(_data.arr.length)
|
|
memcpy(bi.base, _data.arr.ptr, _data.arr.length * T.sizeof);
|
|
_data.arr = (cast(Unqual!(T)*)bi.base)[0.._data.arr.length];
|
|
}
|
|
}
|
|
|
|
/**
|
|
Returns the capacity of the array (the maximum number of elements the
|
|
managed array can accommodate before triggering a reallocation). If any
|
|
appending will reallocate, capacity returns 0.
|
|
*/
|
|
@property size_t capacity()
|
|
{
|
|
return _data ? _data.capacity : 0;
|
|
}
|
|
|
|
/**
|
|
Returns the managed array.
|
|
*/
|
|
@property T[] data()
|
|
{
|
|
return cast(typeof(return))(_data ? _data.arr : null);
|
|
}
|
|
|
|
/**
|
|
Appends one item to the managed array.
|
|
*/
|
|
void put(U)(U item) if (isImplicitlyConvertible!(U, T) ||
|
|
isSomeChar!T && isSomeChar!U)
|
|
{
|
|
static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof)
|
|
{
|
|
// must do some transcoding around here
|
|
Unqual!T[T.sizeof == 1 ? 4 : 2] encoded;
|
|
auto len = std.utf.encode(encoded, item);
|
|
put(encoded[0 .. len]);
|
|
}
|
|
else
|
|
{
|
|
if (!_data)
|
|
_data = new Data;
|
|
immutable len = _data.arr.length;
|
|
if (len >= _data.capacity)
|
|
{
|
|
// Time to reallocate.
|
|
// We need to almost duplicate what's in druntime, except we
|
|
// have better access to the capacity field.
|
|
auto newlen = newCapacity(len + 1);
|
|
// first, try extending the current block
|
|
auto u = GC.extend(_data.arr.ptr, T.sizeof, (newlen - len) * T.sizeof);
|
|
if(u)
|
|
{
|
|
// extend worked, update the capacity
|
|
_data.capacity = u / T.sizeof;
|
|
_data.arr = _data.arr.ptr[0..len + 1];
|
|
}
|
|
else
|
|
{
|
|
// didn't work, must reallocate
|
|
auto bi = GC.qalloc(newlen * T.sizeof, (typeid(T[]).next.flags & 1) ? GC.BlkAttr.NO_SCAN : 0);
|
|
_data.capacity = bi.size / T.sizeof;
|
|
if(len)
|
|
memcpy(bi.base, _data.arr.ptr, len * T.sizeof);
|
|
_data.arr = (cast(Unqual!(T)*)bi.base)[0..len + 1];
|
|
// leave the old data, for safety reasons
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_data.arr = _data.arr.ptr[0 .. len + 1];
|
|
}
|
|
_data.arr.ptr[len] = cast(Unqual!T)item;
|
|
}
|
|
}
|
|
|
|
private static size_t newCapacity(size_t newlength)
|
|
{
|
|
long mult = 100 + (1000L) / (bsr(newlength * T.sizeof) + 1);
|
|
// limit to doubling the length, we don't want to grow too much
|
|
if(mult > 200)
|
|
mult = 200;
|
|
auto newext = cast(size_t)((newlength * mult + 99) / 100);
|
|
return newext > newlength ? newext : newlength;
|
|
}
|
|
|
|
/**
|
|
Appends an entire range to the managed array.
|
|
*/
|
|
void put(Range)(Range items) if (isForwardRange!Range
|
|
&& is(typeof(Appender.init.put(items.front))))
|
|
{
|
|
// note, we disable this branch for appending one type of char to
|
|
// another because we can't trust the length portion.
|
|
static if (!(isSomeChar!T && isSomeChar!(ElementType!Range) &&
|
|
!is(Range == Unqual!(T)[])) &&
|
|
is(typeof(items.length) == size_t))
|
|
{
|
|
// make sure we have enough space, then add the items
|
|
immutable len = _data ? _data.arr.length : 0;
|
|
immutable newlen = len + items.length;
|
|
reserve(newlen);
|
|
_data.arr = _data.arr.ptr[0..newlen];
|
|
static if(is(typeof(_data.arr[] = items)))
|
|
{
|
|
_data.arr.ptr[len..newlen] = items;
|
|
}
|
|
else
|
|
{
|
|
for(size_t i = len; !items.empty; items.popFront(), ++i)
|
|
_data.arr.ptr[i] = items.front;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//pragma(msg, Range.stringof);
|
|
// Generic input range
|
|
for (; !items.empty; items.popFront())
|
|
{
|
|
put(items.front);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Clears the managed array.
|
|
*/
|
|
void clear()
|
|
{
|
|
if (_data)
|
|
{
|
|
_data.arr = _data.arr.ptr[0..0];
|
|
}
|
|
}
|
|
|
|
/**
|
|
Shrinks the managed array to the given length. Passing in a length
|
|
that's greater than the current array length throws an enforce exception.
|
|
*/
|
|
void shrinkTo(size_t newlength)
|
|
{
|
|
if(_data)
|
|
{
|
|
enforce(newlength <= _data.arr.length);
|
|
_data.arr = _data.arr.ptr[0..newlength];
|
|
}
|
|
else
|
|
enforce(newlength == 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Convenience function that returns an $(D Appender!(T)) object
|
|
initialized with $(D t).
|
|
*/
|
|
Appender!(E[]) appender(A : E[], E)(A array = null)
|
|
{
|
|
return Appender!(E[])(array);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
version(none)
|
|
{
|
|
auto arr = new char[0];
|
|
auto app = appender(&arr);
|
|
}
|
|
else
|
|
auto app = appender!(char[])();
|
|
string b = "abcdefg";
|
|
foreach (char c; b) app.put(c);
|
|
assert(app.data == "abcdefg");
|
|
|
|
int[] a = [ 1, 2 ];
|
|
version(none)
|
|
auto app2 = appender(&a);
|
|
else
|
|
auto app2 = appender(a);
|
|
assert(app2.data == [ 1, 2 ]);
|
|
app2.put(3);
|
|
app2.put([ 4, 5, 6 ][]);
|
|
assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]);
|
|
}
|
|
|
|
/*
|
|
A simple slice type only holding pointers to the beginning and the end
|
|
of an array. Experimental duplication of the built-in slice - do not
|
|
use yet.
|
|
*/
|
|
struct SimpleSlice(T)
|
|
{
|
|
private T * _b, _e;
|
|
|
|
this(U...)(U values)
|
|
{
|
|
_b = cast(T*) core.memory.GC.malloc(U.length * T.sizeof);
|
|
_e = _b + U.length;
|
|
foreach (i, Unused; U) _b[i] = values[i];
|
|
}
|
|
|
|
void opAssign(R)(R anotherSlice)
|
|
{
|
|
static if (is(typeof(*_b = anotherSlice)))
|
|
{
|
|
// assign all elements to a value
|
|
foreach (p; _b .. _e)
|
|
{
|
|
*p = anotherSlice;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// assign another slice to this
|
|
enforce(anotherSlice.length == length);
|
|
auto p = _b;
|
|
foreach (p; _b .. _e)
|
|
{
|
|
*p = anotherSlice.front;
|
|
anotherSlice.popFront;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Range primitives.
|
|
*/
|
|
bool empty() const
|
|
{
|
|
assert(_b <= _e);
|
|
return _b == _e;
|
|
}
|
|
|
|
/// Ditto
|
|
ref T front()
|
|
{
|
|
assert(!empty);
|
|
return *_b;
|
|
}
|
|
|
|
/// Ditto
|
|
void popFront()
|
|
{
|
|
assert(!empty);
|
|
++_b;
|
|
}
|
|
|
|
/// Ditto
|
|
ref T back()
|
|
{
|
|
assert(!empty);
|
|
return _e[-1];
|
|
}
|
|
|
|
/// Ditto
|
|
void popBack()
|
|
{
|
|
assert(!empty);
|
|
--_e;
|
|
}
|
|
|
|
/// Ditto
|
|
T opIndex(size_t n)
|
|
{
|
|
assert(n < length);
|
|
return _b[n];
|
|
}
|
|
|
|
/// Ditto
|
|
const(T) opIndex(size_t n) const
|
|
{
|
|
assert(n < length);
|
|
return _b[n];
|
|
}
|
|
|
|
/// Ditto
|
|
void opIndexAssign(T value, size_t n)
|
|
{
|
|
assert(n < length);
|
|
_b[n] = value;
|
|
}
|
|
|
|
/// Ditto
|
|
SimpleSliceLvalue!T opSlice()
|
|
{
|
|
typeof(return) result = void;
|
|
result._b = _b;
|
|
result._e = _e;
|
|
return result;
|
|
}
|
|
|
|
/// Ditto
|
|
SimpleSliceLvalue!T opSlice(size_t x, size_t y)
|
|
{
|
|
enforce(x <= y && y <= length);
|
|
typeof(return) result = { _b + x, _b + y };
|
|
return result;
|
|
}
|
|
|
|
@property
|
|
{
|
|
/// Returns the length of the slice.
|
|
size_t length() const
|
|
{
|
|
return _e - _b;
|
|
}
|
|
|
|
/**
|
|
Sets the length of the slice. Newly added elements will be filled with
|
|
$(D T.init).
|
|
*/
|
|
void length(size_t newLength)
|
|
{
|
|
immutable oldLength = length;
|
|
_b = cast(T*) core.memory.GC.realloc(_b, newLength * T.sizeof);
|
|
_e = _b + newLength;
|
|
this[oldLength .. $] = T.init;
|
|
}
|
|
}
|
|
|
|
/// Concatenation.
|
|
SimpleSlice opCat(R)(R another)
|
|
{
|
|
immutable newLen = length + another.length;
|
|
typeof(return) result = void;
|
|
result._b = cast(T*)
|
|
core.memory.GC.malloc(newLen * T.sizeof);
|
|
result._e = result._b + newLen;
|
|
result[0 .. this.length] = this;
|
|
result[this.length .. result.length] = another;
|
|
return result;
|
|
}
|
|
|
|
/// Concatenation with rebinding.
|
|
void opCatAssign(R)(R another)
|
|
{
|
|
auto newThis = this ~ another;
|
|
move(newThis, this);
|
|
}
|
|
}
|
|
|
|
// Support for mass assignment
|
|
struct SimpleSliceLvalue(T)
|
|
{
|
|
private SimpleSlice!T _s;
|
|
alias _s this;
|
|
|
|
void opAssign(R)(R anotherSlice)
|
|
{
|
|
static if (is(typeof(*_b = anotherSlice)))
|
|
{
|
|
// assign all elements to a value
|
|
foreach (p; _b .. _e)
|
|
{
|
|
*p = anotherSlice;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// assign another slice to this
|
|
enforce(anotherSlice.length == length);
|
|
auto p = _b;
|
|
foreach (p; _b .. _e)
|
|
{
|
|
*p = anotherSlice.front;
|
|
anotherSlice.popFront;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
// SimpleSlice!(int) s;
|
|
|
|
// s = SimpleSlice!(int)(4, 5, 6);
|
|
// assert(equal(s, [4, 5, 6][]));
|
|
// assert(s.length == 3);
|
|
// assert(s[0] == 4);
|
|
// assert(s[1] == 5);
|
|
// assert(s[2] == 6);
|
|
|
|
// assert(s[] == s);
|
|
// assert(s[0 .. s.length] == s);
|
|
// assert(equal(s[0 .. s.length - 1], [4, 5][]));
|
|
|
|
// auto s1 = s ~ s[0 .. 1];
|
|
// assert(equal(s1, [4, 5, 6, 4][]));
|
|
|
|
// assert(s1[3] == 4);
|
|
// s1[3] = 42;
|
|
// assert(s1[3] == 42);
|
|
|
|
// const s2 = s;
|
|
// assert(s2.length == 3);
|
|
// assert(!s2.empty);
|
|
// assert(s2[0] == s[0]);
|
|
|
|
// s[0 .. 2] = 10;
|
|
// assert(equal(s, [10, 10, 6][]));
|
|
|
|
// s ~= [ 5, 9 ][];
|
|
// assert(equal(s, [10, 10, 6, 5, 9][]));
|
|
|
|
// s.length = 7;
|
|
// assert(equal(s, [10, 10, 6, 5, 9, 0, 0][]));
|
|
}
|