mirror of
https://github.com/dlang/phobos.git
synced 2025-05-02 08:00:48 +03:00
[Static if] replace overload constraints with static if (mutation.d)
This commit is contained in:
parent
78acf07136
commit
c6819e2d6e
1 changed files with 260 additions and 267 deletions
|
@ -368,57 +368,61 @@ See_Also:
|
||||||
$(HTTP sgi.com/tech/stl/_copy.html, STL's _copy)
|
$(HTTP sgi.com/tech/stl/_copy.html, STL's _copy)
|
||||||
*/
|
*/
|
||||||
TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
|
TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
|
||||||
if (areCopyCompatibleArrays!(SourceRange, TargetRange))
|
if (isInputRange!SourceRange)
|
||||||
{
|
{
|
||||||
const tlen = target.length;
|
static if (areCopyCompatibleArrays!(SourceRange, TargetRange))
|
||||||
const slen = source.length;
|
|
||||||
assert(tlen >= slen,
|
|
||||||
"Cannot copy a source range into a smaller target range.");
|
|
||||||
|
|
||||||
immutable overlaps = __ctfe || () @trusted {
|
|
||||||
return source.ptr < target.ptr + tlen &&
|
|
||||||
target.ptr < source.ptr + slen; }();
|
|
||||||
|
|
||||||
if (overlaps)
|
|
||||||
{
|
{
|
||||||
foreach (idx; 0 .. slen)
|
const tlen = target.length;
|
||||||
target[idx] = source[idx];
|
const slen = source.length;
|
||||||
return target[slen .. tlen];
|
assert(tlen >= slen,
|
||||||
|
"Cannot copy a source range into a smaller target range.");
|
||||||
|
|
||||||
|
immutable overlaps = __ctfe || () @trusted {
|
||||||
|
return source.ptr < target.ptr + tlen &&
|
||||||
|
target.ptr < source.ptr + slen; }();
|
||||||
|
|
||||||
|
if (overlaps)
|
||||||
|
{
|
||||||
|
foreach (idx; 0 .. slen)
|
||||||
|
target[idx] = source[idx];
|
||||||
|
return target[slen .. tlen];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Array specialization. This uses optimized memory copying
|
||||||
|
// routines under the hood and is about 10-20x faster than the
|
||||||
|
// generic implementation.
|
||||||
|
target[0 .. slen] = source[];
|
||||||
|
return target[slen .. $];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else static if (isOutputRange!(TargetRange, ElementType!SourceRange))
|
||||||
|
{
|
||||||
|
// Specialize for 2 random access ranges.
|
||||||
|
// Typically 2 random access ranges are faster iterated by common
|
||||||
|
// index than by x.popFront(), y.popFront() pair
|
||||||
|
static if (isRandomAccessRange!SourceRange &&
|
||||||
|
hasLength!SourceRange &&
|
||||||
|
hasSlicing!TargetRange &&
|
||||||
|
isRandomAccessRange!TargetRange &&
|
||||||
|
hasLength!TargetRange)
|
||||||
|
{
|
||||||
|
auto len = source.length;
|
||||||
|
foreach (idx; 0 .. len)
|
||||||
|
target[idx] = source[idx];
|
||||||
|
return target[len .. target.length];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
put(target, source);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Array specialization. This uses optimized memory copying
|
enum msg = "TargetRange is neither copy-compatible with the SourceRange" ~
|
||||||
// routines under the hood and is about 10-20x faster than the
|
"nor an OutputRange with the same ElementType.";
|
||||||
// generic implementation.
|
static assert(0, msg);
|
||||||
target[0 .. slen] = source[];
|
|
||||||
return target[slen .. $];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ditto
|
|
||||||
TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
|
|
||||||
if (!areCopyCompatibleArrays!(SourceRange, TargetRange) &&
|
|
||||||
isInputRange!SourceRange &&
|
|
||||||
isOutputRange!(TargetRange, ElementType!SourceRange))
|
|
||||||
{
|
|
||||||
// Specialize for 2 random access ranges.
|
|
||||||
// Typically 2 random access ranges are faster iterated by common
|
|
||||||
// index than by x.popFront(), y.popFront() pair
|
|
||||||
static if (isRandomAccessRange!SourceRange &&
|
|
||||||
hasLength!SourceRange &&
|
|
||||||
hasSlicing!TargetRange &&
|
|
||||||
isRandomAccessRange!TargetRange &&
|
|
||||||
hasLength!TargetRange)
|
|
||||||
{
|
|
||||||
auto len = source.length;
|
|
||||||
foreach (idx; 0 .. len)
|
|
||||||
target[idx] = source[idx];
|
|
||||||
return target[len .. target.length];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
put(target, source);
|
|
||||||
return target;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -843,59 +847,60 @@ See_Also:
|
||||||
$(LREF uninitializeFill)
|
$(LREF uninitializeFill)
|
||||||
*/
|
*/
|
||||||
void initializeAll(Range)(Range range)
|
void initializeAll(Range)(Range range)
|
||||||
if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range)
|
if (isInputRange!Range)
|
||||||
{
|
{
|
||||||
import core.stdc.string : memset, memcpy;
|
static if (hasLvalueElements!Range && hasAssignableElements!Range)
|
||||||
import std.traits : hasElaborateAssign, isDynamicArray;
|
|
||||||
|
|
||||||
alias T = ElementType!Range;
|
|
||||||
static if (hasElaborateAssign!T)
|
|
||||||
{
|
{
|
||||||
import std.algorithm.internal : addressOf;
|
import core.stdc.string : memset, memcpy;
|
||||||
//Elaborate opAssign. Must go the memcpy road.
|
import std.traits : hasElaborateAssign, isDynamicArray;
|
||||||
//We avoid calling emplace here, because our goal is to initialize to
|
|
||||||
//the static state of T.init,
|
alias T = ElementType!Range;
|
||||||
//So we want to avoid any un-necassarilly CC'ing of T.init
|
static if (hasElaborateAssign!T)
|
||||||
auto p = typeid(T).initializer();
|
|
||||||
if (p.ptr)
|
|
||||||
{
|
{
|
||||||
for ( ; !range.empty ; range.popFront() )
|
import std.algorithm.internal : addressOf;
|
||||||
|
//Elaborate opAssign. Must go the memcpy road.
|
||||||
|
//We avoid calling emplace here, because our goal is to initialize to
|
||||||
|
//the static state of T.init,
|
||||||
|
//So we want to avoid any un-necassarilly CC'ing of T.init
|
||||||
|
auto p = typeid(T).initializer();
|
||||||
|
if (p.ptr)
|
||||||
{
|
{
|
||||||
static if (__traits(isStaticArray, T))
|
for ( ; !range.empty ; range.popFront() )
|
||||||
{
|
{
|
||||||
// static array initializer only contains initialization
|
static if (__traits(isStaticArray, T))
|
||||||
// for one element of the static array.
|
|
||||||
auto elemp = cast(void *) addressOf(range.front);
|
|
||||||
auto endp = elemp + T.sizeof;
|
|
||||||
while (elemp < endp)
|
|
||||||
{
|
{
|
||||||
memcpy(elemp, p.ptr, p.length);
|
// static array initializer only contains initialization
|
||||||
elemp += p.length;
|
// for one element of the static array.
|
||||||
|
auto elemp = cast(void *) addressOf(range.front);
|
||||||
|
auto endp = elemp + T.sizeof;
|
||||||
|
while (elemp < endp)
|
||||||
|
{
|
||||||
|
memcpy(elemp, p.ptr, p.length);
|
||||||
|
elemp += p.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(addressOf(range.front), p.ptr, T.sizeof);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(addressOf(range.front), p.ptr, T.sizeof);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
static if (isDynamicArray!Range)
|
||||||
|
memset(range.ptr, 0, range.length * T.sizeof);
|
||||||
|
else
|
||||||
|
for ( ; !range.empty ; range.popFront() )
|
||||||
|
memset(addressOf(range.front), 0, T.sizeof);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
static if (isDynamicArray!Range)
|
fill(range, T.init);
|
||||||
memset(range.ptr, 0, range.length * T.sizeof);
|
|
||||||
else
|
|
||||||
for ( ; !range.empty ; range.popFront() )
|
|
||||||
memset(addressOf(range.front), 0, T.sizeof);
|
|
||||||
}
|
}
|
||||||
else
|
else static if (is(Range == char[]) || is(Range == wchar[]))
|
||||||
fill(range, T.init);
|
{
|
||||||
}
|
alias T = ElementEncodingType!Range;
|
||||||
|
range[] = T.init;
|
||||||
/// ditto
|
}
|
||||||
void initializeAll(Range)(Range range)
|
else static assert(0, "Range doesn't have assignable elements.");
|
||||||
if (is(Range == char[]) || is(Range == wchar[]))
|
|
||||||
{
|
|
||||||
alias T = ElementEncodingType!Range;
|
|
||||||
range[] = T.init;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -1745,138 +1750,133 @@ Returns:
|
||||||
Range remove
|
Range remove
|
||||||
(SwapStrategy s = SwapStrategy.stable, Range, Offset...)
|
(SwapStrategy s = SwapStrategy.stable, Range, Offset...)
|
||||||
(Range range, Offset offset)
|
(Range range, Offset offset)
|
||||||
if (s != SwapStrategy.stable
|
if (isBidirectionalRange!Range
|
||||||
&& isBidirectionalRange!Range
|
|
||||||
&& hasLvalueElements!Range
|
&& hasLvalueElements!Range
|
||||||
&& hasLength!Range
|
&& hasLength!Range
|
||||||
&& Offset.length >= 1)
|
&& Offset.length >= 1)
|
||||||
{
|
{
|
||||||
Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts;
|
static if (s == SwapStrategy.unstable)
|
||||||
foreach (i, v; offset)
|
|
||||||
{
|
{
|
||||||
static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t))
|
Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts;
|
||||||
|
foreach (i, v; offset)
|
||||||
{
|
{
|
||||||
blackouts[i].pos = v[0];
|
static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t))
|
||||||
blackouts[i].len = v[1] - v[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static assert(is(typeof(v) : size_t), typeof(v).stringof);
|
|
||||||
blackouts[i].pos = v;
|
|
||||||
blackouts[i].len = 1;
|
|
||||||
}
|
|
||||||
static if (i > 0)
|
|
||||||
{
|
|
||||||
import std.exception : enforce;
|
|
||||||
|
|
||||||
enforce(blackouts[i - 1].pos + blackouts[i - 1].len
|
|
||||||
<= blackouts[i].pos,
|
|
||||||
"remove(): incorrect ordering of elements to remove");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t left = 0, right = offset.length - 1;
|
|
||||||
auto tgt = range.save;
|
|
||||||
size_t tgtPos = 0;
|
|
||||||
|
|
||||||
while (left <= right)
|
|
||||||
{
|
|
||||||
// Look for a blackout on the right
|
|
||||||
if (blackouts[right].pos + blackouts[right].len >= range.length)
|
|
||||||
{
|
|
||||||
range.popBackExactly(blackouts[right].len);
|
|
||||||
|
|
||||||
// Since right is unsigned, we must check for this case, otherwise
|
|
||||||
// we might turn it into size_t.max and the loop condition will not
|
|
||||||
// fail when it should.
|
|
||||||
if (right > 0)
|
|
||||||
{
|
{
|
||||||
--right;
|
blackouts[i].pos = v[0];
|
||||||
continue;
|
blackouts[i].len = v[1] - v[0];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Advance to next blackout on the left
|
|
||||||
assert(blackouts[left].pos >= tgtPos);
|
|
||||||
tgt.popFrontExactly(blackouts[left].pos - tgtPos);
|
|
||||||
tgtPos = blackouts[left].pos;
|
|
||||||
|
|
||||||
// Number of elements to the right of blackouts[right]
|
|
||||||
immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len);
|
|
||||||
size_t toMove = void;
|
|
||||||
if (tailLen < blackouts[left].len)
|
|
||||||
{
|
|
||||||
toMove = tailLen;
|
|
||||||
blackouts[left].pos += toMove;
|
|
||||||
blackouts[left].len -= toMove;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
toMove = blackouts[left].len;
|
|
||||||
++left;
|
|
||||||
}
|
|
||||||
tgtPos += toMove;
|
|
||||||
foreach (i; 0 .. toMove)
|
|
||||||
{
|
|
||||||
move(range.back, tgt.front);
|
|
||||||
range.popBack();
|
|
||||||
tgt.popFront();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return range;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ditto
|
|
||||||
Range remove
|
|
||||||
(SwapStrategy s = SwapStrategy.stable, Range, Offset...)
|
|
||||||
(Range range, Offset offset)
|
|
||||||
if (s == SwapStrategy.stable
|
|
||||||
&& isBidirectionalRange!Range
|
|
||||||
&& hasLvalueElements!Range
|
|
||||||
&& Offset.length >= 1)
|
|
||||||
{
|
|
||||||
auto result = range;
|
|
||||||
auto src = range, tgt = range;
|
|
||||||
size_t pos;
|
|
||||||
foreach (pass, i; offset)
|
|
||||||
{
|
|
||||||
static if (is(typeof(i[0])) && is(typeof(i[1])))
|
|
||||||
{
|
|
||||||
auto from = i[0], delta = i[1] - i[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto from = i;
|
|
||||||
enum delta = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (pass > 0)
|
|
||||||
{
|
|
||||||
import std.exception : enforce;
|
|
||||||
enforce(pos <= from,
|
|
||||||
"remove(): incorrect ordering of elements to remove");
|
|
||||||
|
|
||||||
for (; pos < from; ++pos, src.popFront(), tgt.popFront())
|
|
||||||
{
|
{
|
||||||
move(src.front, tgt.front);
|
static assert(is(typeof(v) : size_t), typeof(v).stringof);
|
||||||
|
blackouts[i].pos = v;
|
||||||
|
blackouts[i].len = 1;
|
||||||
|
}
|
||||||
|
static if (i > 0)
|
||||||
|
{
|
||||||
|
import std.exception : enforce;
|
||||||
|
|
||||||
|
enforce(blackouts[i - 1].pos + blackouts[i - 1].len
|
||||||
|
<= blackouts[i].pos,
|
||||||
|
"remove(): incorrect ordering of elements to remove");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
size_t left = 0, right = offset.length - 1;
|
||||||
|
auto tgt = range.save;
|
||||||
|
size_t tgtPos = 0;
|
||||||
|
|
||||||
|
while (left <= right)
|
||||||
{
|
{
|
||||||
src.popFrontExactly(from);
|
// Look for a blackout on the right
|
||||||
tgt.popFrontExactly(from);
|
if (blackouts[right].pos + blackouts[right].len >= range.length)
|
||||||
pos = from;
|
{
|
||||||
|
range.popBackExactly(blackouts[right].len);
|
||||||
|
|
||||||
|
// Since right is unsigned, we must check for this case, otherwise
|
||||||
|
// we might turn it into size_t.max and the loop condition will not
|
||||||
|
// fail when it should.
|
||||||
|
if (right > 0)
|
||||||
|
{
|
||||||
|
--right;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Advance to next blackout on the left
|
||||||
|
assert(blackouts[left].pos >= tgtPos);
|
||||||
|
tgt.popFrontExactly(blackouts[left].pos - tgtPos);
|
||||||
|
tgtPos = blackouts[left].pos;
|
||||||
|
|
||||||
|
// Number of elements to the right of blackouts[right]
|
||||||
|
immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len);
|
||||||
|
size_t toMove = void;
|
||||||
|
if (tailLen < blackouts[left].len)
|
||||||
|
{
|
||||||
|
toMove = tailLen;
|
||||||
|
blackouts[left].pos += toMove;
|
||||||
|
blackouts[left].len -= toMove;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toMove = blackouts[left].len;
|
||||||
|
++left;
|
||||||
|
}
|
||||||
|
tgtPos += toMove;
|
||||||
|
foreach (i; 0 .. toMove)
|
||||||
|
{
|
||||||
|
move(range.back, tgt.front);
|
||||||
|
range.popBack();
|
||||||
|
tgt.popFront();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// now skip source to the "to" position
|
|
||||||
src.popFrontExactly(delta);
|
return range;
|
||||||
result.popBackExactly(delta);
|
|
||||||
pos += delta;
|
|
||||||
}
|
}
|
||||||
// leftover move
|
else static if (s == SwapStrategy.stable)
|
||||||
moveAll(src, tgt);
|
{
|
||||||
return result;
|
auto result = range;
|
||||||
|
auto src = range, tgt = range;
|
||||||
|
size_t pos;
|
||||||
|
foreach (pass, i; offset)
|
||||||
|
{
|
||||||
|
static if (is(typeof(i[0])) && is(typeof(i[1])))
|
||||||
|
{
|
||||||
|
auto from = i[0], delta = i[1] - i[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto from = i;
|
||||||
|
enum delta = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (pass > 0)
|
||||||
|
{
|
||||||
|
import std.exception : enforce;
|
||||||
|
enforce(pos <= from,
|
||||||
|
"remove(): incorrect ordering of elements to remove");
|
||||||
|
|
||||||
|
for (; pos < from; ++pos, src.popFront(), tgt.popFront())
|
||||||
|
{
|
||||||
|
move(src.front, tgt.front);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
src.popFrontExactly(from);
|
||||||
|
tgt.popFrontExactly(from);
|
||||||
|
pos = from;
|
||||||
|
}
|
||||||
|
// now skip source to the "to" position
|
||||||
|
src.popFrontExactly(delta);
|
||||||
|
result.popBackExactly(delta);
|
||||||
|
pos += delta;
|
||||||
|
}
|
||||||
|
// leftover move
|
||||||
|
moveAll(src, tgt);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else static assert(0, "SwapStrategy.semistable is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -2132,24 +2132,67 @@ if (isBidirectionalRange!Range
|
||||||
/**
|
/**
|
||||||
Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D
|
Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D
|
||||||
swap).
|
swap).
|
||||||
|
UTF sequences consisting of multiple code units are preserved properly.
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
|
r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
|
||||||
with swappable elements or a random access range with a length member
|
with swappable elements, a random access range with a length member or a
|
||||||
|
narrow string.
|
||||||
|
|
||||||
See_Also:
|
See_Also:
|
||||||
$(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view
|
$(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view
|
||||||
|
|
||||||
|
Bugs:
|
||||||
|
When passing a sting with unicode modifiers on characters, such as $(D \u0301),
|
||||||
|
this function will not properly keep the position of the modifier. For example,
|
||||||
|
reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("d́ab") instead of
|
||||||
|
$(D da\u0301b) ("dáb").
|
||||||
*/
|
*/
|
||||||
void reverse(Range)(Range r)
|
void reverse(Range)(Range r)
|
||||||
if (isBidirectionalRange!Range && !isRandomAccessRange!Range
|
if (isBidirectionalRange!Range)
|
||||||
&& hasSwappableElements!Range)
|
|
||||||
{
|
{
|
||||||
while (!r.empty)
|
static if (isRandomAccessRange!Range)
|
||||||
{
|
{
|
||||||
swap(r.front, r.back);
|
//swapAt is in fact the only way to swap non lvalue ranges
|
||||||
r.popFront();
|
immutable last = r.length-1;
|
||||||
if (r.empty) break;
|
immutable steps = r.length/2;
|
||||||
r.popBack();
|
for (size_t i = 0; i < steps; i++)
|
||||||
|
{
|
||||||
|
r.swapAt(i, last-i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else static if (hasSwappableElements!Range)
|
||||||
|
{
|
||||||
|
while (!r.empty)
|
||||||
|
{
|
||||||
|
swap(r.front, r.back);
|
||||||
|
r.popFront();
|
||||||
|
if (r.empty) break;
|
||||||
|
r.popBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else static if (isNarrowString!Range && !is(ElementType!Range == const) && !is(ElementType!Range == immutable))
|
||||||
|
{
|
||||||
|
import std.string : representation;
|
||||||
|
import std.utf : stride;
|
||||||
|
|
||||||
|
auto repr = representation(r);
|
||||||
|
for (size_t i = 0; i < r.length; )
|
||||||
|
{
|
||||||
|
immutable step = stride(r, i);
|
||||||
|
if (step > 1)
|
||||||
|
{
|
||||||
|
.reverse(repr[i .. i + step]);
|
||||||
|
i += step;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reverse(repr);
|
||||||
|
}
|
||||||
|
else static assert(0, "Range is neither RandomAccess, has swappable elements nor a narrow string.");
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -2160,17 +2203,12 @@ if (isBidirectionalRange!Range && !isRandomAccessRange!Range
|
||||||
assert(arr == [ 3, 2, 1 ]);
|
assert(arr == [ 3, 2, 1 ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
///ditto
|
///
|
||||||
void reverse(Range)(Range r)
|
@safe unittest
|
||||||
if (isRandomAccessRange!Range && hasLength!Range)
|
|
||||||
{
|
{
|
||||||
//swapAt is in fact the only way to swap non lvalue ranges
|
char[] arr = "hello\U00010143\u0100\U00010143".dup;
|
||||||
immutable last = r.length-1;
|
reverse(arr);
|
||||||
immutable steps = r.length/2;
|
assert(arr == "\U00010143\u0100\U00010143olleh");
|
||||||
for (size_t i = 0; i < steps; i++)
|
|
||||||
{
|
|
||||||
r.swapAt(i, last-i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@safe unittest
|
@safe unittest
|
||||||
|
@ -2188,51 +2226,6 @@ if (isRandomAccessRange!Range && hasLength!Range)
|
||||||
assert(range == [3, 2, 1]);
|
assert(range == [3, 2, 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Reverses $(D r) in-place, where $(D r) is a narrow string (having
|
|
||||||
elements of type $(D char) or $(D wchar)). UTF sequences consisting of
|
|
||||||
multiple code units are preserved properly.
|
|
||||||
|
|
||||||
Params:
|
|
||||||
s = a narrow string
|
|
||||||
|
|
||||||
Bugs:
|
|
||||||
When passing a sting with unicode modifiers on characters, such as $(D \u0301),
|
|
||||||
this function will not properly keep the position of the modifier. For example,
|
|
||||||
reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("d́ab") instead of
|
|
||||||
$(D da\u0301b) ("dáb").
|
|
||||||
*/
|
|
||||||
void reverse(Char)(Char[] s)
|
|
||||||
if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable))
|
|
||||||
{
|
|
||||||
import std.string : representation;
|
|
||||||
import std.utf : stride;
|
|
||||||
|
|
||||||
auto r = representation(s);
|
|
||||||
for (size_t i = 0; i < s.length; )
|
|
||||||
{
|
|
||||||
immutable step = stride(s, i);
|
|
||||||
if (step > 1)
|
|
||||||
{
|
|
||||||
.reverse(r[i .. i + step]);
|
|
||||||
i += step;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reverse(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
@safe unittest
|
|
||||||
{
|
|
||||||
char[] arr = "hello\U00010143\u0100\U00010143".dup;
|
|
||||||
reverse(arr);
|
|
||||||
assert(arr == "\U00010143\u0100\U00010143olleh");
|
|
||||||
}
|
|
||||||
|
|
||||||
@safe unittest
|
@safe unittest
|
||||||
{
|
{
|
||||||
void test(string a, string b)
|
void test(string a, string b)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue