mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 14:40:30 +03:00
Merge remote-tracking branch 'upstream/master' into stable
Conflicts: posix.mak
This commit is contained in:
commit
498b4b97d6
56 changed files with 1675 additions and 1111 deletions
|
@ -623,7 +623,8 @@ if (isInputRange!R1 && isInputRange!R2)
|
|||
static if (isDynamicArray!R1 && isDynamicArray!R2
|
||||
&& __traits(isUnsigned, E1) && __traits(isUnsigned, E2)
|
||||
&& E1.sizeof == 1 && E2.sizeof == 1
|
||||
&& (is(Unqual!E1 == char) == is(Unqual!E2 == char))) // Both or neither must auto-decode.
|
||||
// Both or neither must auto-decode.
|
||||
&& (is(immutable E1 == immutable char) == is(immutable E2 == immutable char)))
|
||||
{
|
||||
// dstrcmp algorithm is correct for both ubyte[] and for char[].
|
||||
import core.internal.string : dstrcmp;
|
||||
|
@ -886,12 +887,23 @@ template equal(alias pred = "a == b")
|
|||
|
||||
enum hasFixedLength(T) = hasLength!T || isNarrowString!T;
|
||||
|
||||
// use code points when comparing two ranges of UTF code units that aren't
|
||||
// the same type. This is for backwards compatibility with autodecode
|
||||
// strings.
|
||||
enum useCodePoint(R1, R2) =
|
||||
isSomeChar!(ElementEncodingType!R1) && isSomeChar!(ElementEncodingType!R2) &&
|
||||
(ElementEncodingType!R1).sizeof != (ElementEncodingType!R2).sizeof;
|
||||
|
||||
/++
|
||||
Compares two ranges for equality. The ranges may have
|
||||
different element types, as long as `pred(r1.front, r2.front)`
|
||||
evaluates to `bool`.
|
||||
Performs $(BIGOH min(r1.length, r2.length)) evaluations of `pred`.
|
||||
|
||||
If the two ranges are different kinds of UTF code unit (`char`, `wchar`, or
|
||||
`dchar`), then the arrays are compared using UTF decoding to avoid
|
||||
accidentally integer-promoting units.
|
||||
|
||||
Params:
|
||||
r1 = The first range to be compared.
|
||||
r2 = The second range to be compared.
|
||||
|
@ -901,7 +913,8 @@ template equal(alias pred = "a == b")
|
|||
for element, according to binary predicate `pred`.
|
||||
+/
|
||||
bool equal(Range1, Range2)(Range1 r1, Range2 r2)
|
||||
if (isInputRange!Range1 && isInputRange!Range2 &&
|
||||
if (!useCodePoint!(Range1, Range2) &&
|
||||
isInputRange!Range1 && isInputRange!Range2 &&
|
||||
is(typeof(binaryFun!pred(r1.front, r2.front))))
|
||||
{
|
||||
static assert(!(isInfinite!Range1 && isInfinite!Range2),
|
||||
|
@ -927,7 +940,7 @@ template equal(alias pred = "a == b")
|
|||
// can be avoided if they have the same ElementEncodingType
|
||||
else static if (is(typeof(pred) == string) && pred == "a == b" &&
|
||||
isAutodecodableString!Range1 != isAutodecodableString!Range2 &&
|
||||
is(ElementEncodingType!Range1 == ElementEncodingType!Range2))
|
||||
is(immutable ElementEncodingType!Range1 == immutable ElementEncodingType!Range2))
|
||||
{
|
||||
import std.utf : byCodeUnit;
|
||||
|
||||
|
@ -967,6 +980,14 @@ template equal(alias pred = "a == b")
|
|||
return r2.empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// ditto
|
||||
bool equal(Range1, Range2)(Range1 r1, Range2 r2)
|
||||
if (useCodePoint!(Range1, Range2))
|
||||
{
|
||||
import std.utf : byDchar;
|
||||
return equal(r1.byDchar, r2.byDchar);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1072,20 +1093,28 @@ range of range (of range...) comparisons.
|
|||
|
||||
@safe @nogc pure unittest
|
||||
{
|
||||
import std.utf : byChar, byDchar;
|
||||
import std.utf : byChar, byDchar, byWchar;
|
||||
|
||||
assert(equal("æøå".byChar, "æøå"));
|
||||
assert(equal("æøå".byChar, "æøå"w));
|
||||
assert(equal("æøå".byChar, "æøå"d));
|
||||
assert(equal("æøå", "æøå".byChar));
|
||||
assert(equal("æøå".byDchar, "æøå"d));
|
||||
assert(equal("æøå"d, "æøå".byDchar));
|
||||
}
|
||||
|
||||
@safe pure unittest
|
||||
{
|
||||
import std.utf : byWchar;
|
||||
assert(equal("æøå"w, "æøå".byChar));
|
||||
assert(equal("æøå"d, "æøå".byChar));
|
||||
|
||||
assert(equal("æøå".byWchar, "æøå"));
|
||||
assert(equal("æøå".byWchar, "æøå"w));
|
||||
assert(equal("æøå".byWchar, "æøå"d));
|
||||
assert(equal("æøå", "æøå".byWchar));
|
||||
assert(equal("æøå"w, "æøå".byWchar));
|
||||
assert(equal("æøå"d, "æøå".byWchar));
|
||||
|
||||
assert(equal("æøå".byDchar, "æøå"));
|
||||
assert(equal("æøå".byDchar, "æøå"w));
|
||||
assert(equal("æøå".byDchar, "æøå"d));
|
||||
assert(equal("æøå", "æøå".byDchar));
|
||||
assert(equal("æøå"w, "æøå".byDchar));
|
||||
assert(equal("æøå"d, "æøå".byDchar));
|
||||
}
|
||||
|
||||
@safe @nogc pure unittest
|
||||
|
@ -1684,8 +1713,8 @@ if (T.length >= 2)
|
|||
|
||||
//Do the "min" proper with a and b
|
||||
import std.functional : lessThan;
|
||||
immutable chooseA = lessThan!(T0, T1)(a, b);
|
||||
return cast(typeof(return)) (chooseA ? a : b);
|
||||
immutable chooseB = lessThan!(T1, T0)(b, a);
|
||||
return cast(typeof(return)) (chooseB ? b : a);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1728,6 +1757,19 @@ store the lowest values.
|
|||
assert(min(Date.max, Date.min) == Date.min);
|
||||
}
|
||||
|
||||
// min must be stable: when in doubt, return the first argument.
|
||||
@safe unittest
|
||||
{
|
||||
assert(min(1.0, double.nan) == 1.0);
|
||||
assert(min(double.nan, 1.0) is double.nan);
|
||||
static struct A {
|
||||
int x;
|
||||
string y;
|
||||
int opCmp(const A a) const { return int(x > a.x) - int(x < a.x); }
|
||||
}
|
||||
assert(min(A(1, "first"), A(1, "second")) == A(1, "first"));
|
||||
}
|
||||
|
||||
// mismatch
|
||||
/**
|
||||
Sequentially compares elements in `r1` and `r2` in lockstep, and
|
||||
|
|
|
@ -5486,8 +5486,8 @@ if (isSomeString!Range ||
|
|||
import std.uni : isWhite;
|
||||
import std.traits : Unqual;
|
||||
|
||||
static if (is(Unqual!(ElementEncodingType!Range) == wchar) &&
|
||||
is(Unqual!(ElementType!Range) == dchar))
|
||||
static if (is(immutable ElementEncodingType!Range == immutable wchar) &&
|
||||
is(immutable ElementType!Range == immutable dchar))
|
||||
{
|
||||
// all unicode whitespace characters fit into a wchar. However,
|
||||
// this range is a wchar array, so we will treat it like a
|
||||
|
@ -5500,8 +5500,8 @@ if (isSomeString!Range ||
|
|||
break;
|
||||
}
|
||||
}
|
||||
else static if (is(Unqual!(ElementType!Range) == dchar) ||
|
||||
is(Unqual!(ElementType!Range) == wchar))
|
||||
else static if (is(immutable ElementType!Range == immutable dchar) ||
|
||||
is(immutable ElementType!Range == immutable wchar))
|
||||
{
|
||||
// dchar or wchar range, we can just use find.
|
||||
auto r = find!(isWhite)(_s.save);
|
||||
|
|
|
@ -233,7 +233,7 @@ if (isInputRange!(Range) && is(typeof(r.front == lPar)))
|
|||
{
|
||||
size_t count;
|
||||
|
||||
static if (is(Unqual!(ElementEncodingType!Range) == Unqual!E) && isNarrowString!Range)
|
||||
static if (is(immutable ElementEncodingType!Range == immutable E) && isNarrowString!Range)
|
||||
{
|
||||
import std.utf : byCodeUnit;
|
||||
auto rn = r.byCodeUnit;
|
||||
|
@ -1106,7 +1106,7 @@ if (isBidirectionalRange!R1 &&
|
|||
enum isDefaultPred = false;
|
||||
|
||||
static if (isDefaultPred && isArray!R1 && isArray!R2 &&
|
||||
is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
|
||||
is(immutable ElementEncodingType!R1 == immutable ElementEncodingType!R2))
|
||||
{
|
||||
if (haystack.length < needle.length) return false;
|
||||
|
||||
|
@ -4614,7 +4614,7 @@ if (isInputRange!Range && Needles.length > 1 &&
|
|||
|
||||
template checkType(T)
|
||||
{
|
||||
enum checkType = is(Unqual!(ElementEncodingType!Range) == Unqual!T);
|
||||
enum checkType = is(immutable ElementEncodingType!Range == immutable T);
|
||||
}
|
||||
|
||||
// auto-decoding special case
|
||||
|
@ -4719,7 +4719,7 @@ if (isInputRange!R1 &&
|
|||
}
|
||||
|
||||
static if (isDefaultPred && isArray!R1 && isArray!R2 &&
|
||||
is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
|
||||
is(immutable ElementEncodingType!R1 == immutable ElementEncodingType!R2))
|
||||
{
|
||||
//Array slice comparison mode
|
||||
return haystack[0 .. needle.length] == needle;
|
||||
|
|
|
@ -1698,17 +1698,30 @@ private void shortSort(alias less, Range)(Range r)
|
|||
auto t = r[0]; if (pred(t, r[0])) r[0] = r[0];
|
||||
}))) // Can we afford to temporarily invalidate the array?
|
||||
{
|
||||
import core.lifetime : move;
|
||||
|
||||
size_t j = i + 1;
|
||||
auto temp = r[i];
|
||||
static if (hasLvalueElements!Range)
|
||||
auto temp = move(r[i]);
|
||||
else
|
||||
auto temp = r[i];
|
||||
|
||||
if (pred(r[j], temp))
|
||||
{
|
||||
do
|
||||
{
|
||||
r[j - 1] = r[j];
|
||||
static if (hasLvalueElements!Range)
|
||||
trustedMoveEmplace(r[j], r[j - 1]);
|
||||
else
|
||||
r[j - 1] = r[j];
|
||||
++j;
|
||||
}
|
||||
while (j < r.length && pred(r[j], temp));
|
||||
r[j - 1] = temp;
|
||||
|
||||
static if (hasLvalueElements!Range)
|
||||
trustedMoveEmplace(temp, r[j - 1]);
|
||||
else
|
||||
r[j - 1] = move(temp);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1725,6 +1738,13 @@ private void shortSort(alias less, Range)(Range r)
|
|||
}
|
||||
}
|
||||
|
||||
/// @trusted wrapper for moveEmplace
|
||||
private void trustedMoveEmplace(T)(ref T source, ref T target) @trusted
|
||||
{
|
||||
import core.lifetime : moveEmplace;
|
||||
moveEmplace(source, target);
|
||||
}
|
||||
|
||||
@safe unittest
|
||||
{
|
||||
import std.random : Random = Xorshift, uniform;
|
||||
|
@ -2274,9 +2294,9 @@ private template TimSortImpl(alias pred, R)
|
|||
alias T = ElementType!R;
|
||||
|
||||
alias less = binaryFun!pred;
|
||||
alias greater = (a, b) => less(b, a);
|
||||
alias greaterEqual = (a, b) => !less(a, b);
|
||||
alias lessEqual = (a, b) => !less(b, a);
|
||||
bool greater()(auto ref T a, auto ref T b) { return less(b, a); }
|
||||
bool greaterEqual()(auto ref T a, auto ref T b) { return !less(a, b); }
|
||||
bool lessEqual()(auto ref T a, auto ref T b) { return !less(b, a); }
|
||||
|
||||
enum minimalMerge = 128;
|
||||
enum minimalGallop = 7;
|
||||
|
@ -2448,8 +2468,17 @@ private template TimSortImpl(alias pred, R)
|
|||
//moveAll(retro(range[lower .. sortedLen]),
|
||||
// retro(range[lower+1 .. sortedLen+1]));
|
||||
for (upper=sortedLen; upper > lower; upper--)
|
||||
range[upper] = range.moveAt(upper - 1);
|
||||
range[lower] = move(item);
|
||||
{
|
||||
static if (hasLvalueElements!R)
|
||||
move(range[upper -1], range[upper]);
|
||||
else
|
||||
range[upper] = range.moveAt(upper - 1);
|
||||
}
|
||||
|
||||
static if (hasLvalueElements!R)
|
||||
move(item, range[lower]);
|
||||
else
|
||||
range[lower] = move(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2555,7 +2584,7 @@ private template TimSortImpl(alias pred, R)
|
|||
copy(range[0 .. mid], temp);
|
||||
|
||||
// Move first element into place
|
||||
range[0] = range[mid];
|
||||
moveEntry(range, mid, range, 0);
|
||||
|
||||
size_t i = 1, lef = 0, rig = mid + 1;
|
||||
size_t count_lef, count_rig;
|
||||
|
@ -2572,14 +2601,14 @@ private template TimSortImpl(alias pred, R)
|
|||
{
|
||||
if (lessEqual(temp[lef], range[rig]))
|
||||
{
|
||||
range[i++] = temp[lef++];
|
||||
moveEntry(temp, lef++, range, i++);
|
||||
if (lef >= lef_end) break outer;
|
||||
++count_lef;
|
||||
count_rig = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
range[i++] = range[rig++];
|
||||
moveEntry(range, rig++, range, i++);
|
||||
if (rig >= range.length) break outer;
|
||||
count_lef = 0;
|
||||
++count_rig;
|
||||
|
@ -2590,14 +2619,14 @@ private template TimSortImpl(alias pred, R)
|
|||
do
|
||||
{
|
||||
count_lef = gallopForwardUpper(temp[lef .. $], range[rig]);
|
||||
foreach (j; 0 .. count_lef) range[i++] = temp[lef++];
|
||||
foreach (j; 0 .. count_lef) moveEntry(temp, lef++, range, i++);
|
||||
if (lef >= temp.length) break outer;
|
||||
|
||||
count_rig = gallopForwardLower(range[rig .. range.length], temp[lef]);
|
||||
foreach (j; 0 .. count_rig) range[i++] = range[rig++];
|
||||
foreach (j; 0 .. count_rig) moveEntry(range, rig++, range, i++);
|
||||
if (rig >= range.length) while (true)
|
||||
{
|
||||
range[i++] = temp[lef++];
|
||||
moveEntry(temp, lef++, range, i++);
|
||||
if (lef >= temp.length) break outer;
|
||||
}
|
||||
|
||||
|
@ -2610,11 +2639,11 @@ private template TimSortImpl(alias pred, R)
|
|||
|
||||
// Move remaining elements from right
|
||||
while (rig < range.length)
|
||||
range[i++] = range[rig++];
|
||||
moveEntry(range, rig++, range, i++);
|
||||
|
||||
// Move remaining elements from left
|
||||
while (lef < temp.length)
|
||||
range[i++] = temp[lef++];
|
||||
moveEntry(temp, lef++, range, i++);
|
||||
|
||||
return minGallop > 0 ? minGallop : 1;
|
||||
}
|
||||
|
@ -2641,7 +2670,7 @@ private template TimSortImpl(alias pred, R)
|
|||
copy(range[mid .. range.length], temp);
|
||||
|
||||
// Move first element into place
|
||||
range[range.length - 1] = range[mid - 1];
|
||||
moveEntry(range, mid - 1, range, range.length - 1);
|
||||
|
||||
size_t i = range.length - 2, lef = mid - 2, rig = temp.length - 1;
|
||||
size_t count_lef, count_rig;
|
||||
|
@ -2657,19 +2686,19 @@ private template TimSortImpl(alias pred, R)
|
|||
{
|
||||
if (greaterEqual(temp[rig], range[lef]))
|
||||
{
|
||||
range[i--] = temp[rig];
|
||||
moveEntry(temp, rig, range, i--);
|
||||
if (rig == 1)
|
||||
{
|
||||
// Move remaining elements from left
|
||||
while (true)
|
||||
{
|
||||
range[i--] = range[lef];
|
||||
moveEntry(range, lef, range, i--);
|
||||
if (lef == 0) break;
|
||||
--lef;
|
||||
}
|
||||
|
||||
// Move last element into place
|
||||
range[i] = temp[0];
|
||||
moveEntry(temp, 0, range, i);
|
||||
|
||||
break outer;
|
||||
}
|
||||
|
@ -2679,10 +2708,10 @@ private template TimSortImpl(alias pred, R)
|
|||
}
|
||||
else
|
||||
{
|
||||
range[i--] = range[lef];
|
||||
moveEntry(range, lef, range, i--);
|
||||
if (lef == 0) while (true)
|
||||
{
|
||||
range[i--] = temp[rig];
|
||||
moveEntry(temp, rig, range, i--);
|
||||
if (rig == 0) break outer;
|
||||
--rig;
|
||||
}
|
||||
|
@ -2698,7 +2727,7 @@ private template TimSortImpl(alias pred, R)
|
|||
count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]);
|
||||
foreach (j; 0 .. count_rig)
|
||||
{
|
||||
range[i--] = temp[rig];
|
||||
moveEntry(temp, rig, range, i--);
|
||||
if (rig == 0) break outer;
|
||||
--rig;
|
||||
}
|
||||
|
@ -2706,10 +2735,10 @@ private template TimSortImpl(alias pred, R)
|
|||
count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]);
|
||||
foreach (j; 0 .. count_lef)
|
||||
{
|
||||
range[i--] = range[lef];
|
||||
moveEntry(range, lef, range, i--);
|
||||
if (lef == 0) while (true)
|
||||
{
|
||||
range[i--] = temp[rig];
|
||||
moveEntry(temp, rig, range, i--);
|
||||
if (rig == 0) break outer;
|
||||
--rig;
|
||||
}
|
||||
|
@ -2806,6 +2835,21 @@ private template TimSortImpl(alias pred, R)
|
|||
alias gallopForwardUpper = gallopSearch!(false, true);
|
||||
alias gallopReverseLower = gallopSearch!( true, false);
|
||||
alias gallopReverseUpper = gallopSearch!( true, true);
|
||||
|
||||
/// Helper method that moves from[fIdx] into to[tIdx] if both are lvalues and
|
||||
/// uses a plain assignment if not (necessary for backwards compatibility)
|
||||
void moveEntry(X, Y)(ref X from, const size_t fIdx, ref Y to, const size_t tIdx)
|
||||
{
|
||||
// This template is instantiated with different combinations of range (R) and temp (T[]).
|
||||
// T[] obviously has lvalue-elements, so checking R should be enough here
|
||||
static if (hasLvalueElements!R)
|
||||
{
|
||||
import core.lifetime : move;
|
||||
move(from[fIdx], to[tIdx]);
|
||||
}
|
||||
else
|
||||
to[tIdx] = from[fIdx];
|
||||
}
|
||||
}
|
||||
|
||||
@safe unittest
|
||||
|
@ -2918,6 +2962,40 @@ private template TimSortImpl(alias pred, R)
|
|||
sort!("a < b", SwapStrategy.stable)(arr);
|
||||
}
|
||||
|
||||
@safe unittest
|
||||
{
|
||||
static struct NoCopy
|
||||
{
|
||||
pure nothrow @nogc @safe:
|
||||
|
||||
int key;
|
||||
this(scope const ref NoCopy)
|
||||
{
|
||||
assert(false, "Tried to copy struct!");
|
||||
}
|
||||
ref opAssign()(scope const auto ref NoCopy other)
|
||||
{
|
||||
assert(false, "Tried to copy struct!");
|
||||
}
|
||||
this(this) {}
|
||||
}
|
||||
|
||||
static NoCopy[] makeArray(const size_t size)
|
||||
{
|
||||
NoCopy[] array = new NoCopy[](size);
|
||||
foreach (const i, ref t; array[0..$/2]) t.key = cast(int) (size - i);
|
||||
foreach (const i, ref t; array[$/2..$]) t.key = cast(int) i;
|
||||
return array;
|
||||
}
|
||||
|
||||
alias cmp = (ref NoCopy a, ref NoCopy b) => a.key < b.key;
|
||||
enum minMerge = TimSortImpl!(cmp, NoCopy[]).minimalMerge;
|
||||
|
||||
sort!(cmp, SwapStrategy.unstable)(makeArray(20));
|
||||
sort!(cmp, SwapStrategy.stable)(makeArray(minMerge - 5));
|
||||
sort!(cmp, SwapStrategy.stable)(makeArray(minMerge + 5));
|
||||
}
|
||||
|
||||
// schwartzSort
|
||||
/**
|
||||
Alternative sorting method that should be used when comparing keys involves an
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue