130 changes: 63 additions & 67 deletions std/meta.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
/**
* Templates to manipulate template argument lists (also known as type lists).
*
* Some operations on type tuples are built in to the language,
* Some operations on alias sequences are built in to the language,
* such as TL[$(I n)] which gets the $(I n)th type from the
* type tuple. TL[$(I lwr) .. $(I upr)] returns a new type
* alias sequence. TL[$(I lwr) .. $(I upr)] returns a new type
* list that is a slice of the old one.
*
* Several templates in this module use or operate on eponymous templates that
Expand All @@ -31,18 +31,19 @@
module std.meta;

/**
* Creates a tuple out of a sequence of zero or more template arguments.
* Creates a sequence of zero or more aliases. This is most commonly
* used as template parameters or arguments.
*/
template Arguments(TList...)
template AliasSeq(TList...)
{
alias Arguments = TList;
alias AliasSeq = TList;
}

///
unittest
{
import std.typetuple;
alias TL = Arguments!(int, double);
import std.meta;
alias TL = AliasSeq!(int, double);

int foo(TL td) // same as int foo(int, double);
{
Expand All @@ -53,17 +54,12 @@ unittest
///
unittest
{
alias TL = Arguments!(int, double);
alias TL = AliasSeq!(int, double);

alias Types = Arguments!(TL, char);
static assert(is(Types == Arguments!(int, double, char)));
alias Types = AliasSeq!(TL, char);
static assert(is(Types == AliasSeq!(int, double, char)));
}

/**
* Alternate name for $(LREF Arguments) for legacy compatibility.
*/
alias TypeTuple = Arguments;

/**
* Returns the index of the first occurrence of type T in the
* sequence of zero or more types TList.
Expand All @@ -88,7 +84,7 @@ unittest
void foo()
{
writefln("The index of long is %s",
staticIndexOf!(long, TypeTuple!(int, long, double)));
staticIndexOf!(long, AliasSeq!(int, long, double)));
// prints: The index of long is 1
}
}
Expand Down Expand Up @@ -164,9 +160,9 @@ template Erase(alias T, TList...)
///
unittest
{
alias Types = TypeTuple!(int, long, double, char);
alias Types = AliasSeq!(int, long, double, char);
alias TL = Erase!(long, Types);
static assert(is(TL == TypeTuple!(int, double, char)));
static assert(is(TL == AliasSeq!(int, double, char)));
}

// [internal]
Expand All @@ -184,11 +180,11 @@ private template GenericErase(args...)
static if (isSame!(e, head))
alias result = tail;
else
alias result = TypeTuple!(head, GenericErase!(e, tail).result);
alias result = AliasSeq!(head, GenericErase!(e, tail).result);
}
else
{
alias result = TypeTuple!();
alias result = AliasSeq!();
}
}

Expand Down Expand Up @@ -222,10 +218,10 @@ template EraseAll(alias T, TList...)
///
unittest
{
alias Types = TypeTuple!(int, long, long, int);
alias Types = AliasSeq!(int, long, long, int);

alias TL = EraseAll!(long, Types);
static assert(is(TL == TypeTuple!(int, int)));
static assert(is(TL == AliasSeq!(int, int)));
}

// [internal]
Expand All @@ -244,11 +240,11 @@ private template GenericEraseAll(args...)
static if (isSame!(e, head))
alias result = next;
else
alias result = TypeTuple!(head, next);
alias result = AliasSeq!(head, next);
}
else
{
alias result = TypeTuple!();
alias result = AliasSeq!();
}
}

Expand All @@ -274,16 +270,16 @@ template NoDuplicates(TList...)
alias NoDuplicates = TList;
else
alias NoDuplicates =
TypeTuple!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $])));
AliasSeq!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $])));
}

///
unittest
{
alias Types = TypeTuple!(int, long, long, int, float);
alias Types = AliasSeq!(int, long, long, int, float);

alias TL = NoDuplicates!(Types);
static assert(is(TL == TypeTuple!(int, long, float)));
static assert(is(TL == AliasSeq!(int, long, float)));
}

unittest
Expand Down Expand Up @@ -325,10 +321,10 @@ template Replace(alias T, alias U, TList...)
///
unittest
{
alias Types = TypeTuple!(int, long, long, int, float);
alias Types = AliasSeq!(int, long, long, int, float);

alias TL = Replace!(long, char, Types);
static assert(is(TL == TypeTuple!(int, char, long, int, float)));
static assert(is(TL == AliasSeq!(int, char, long, int, float)));
}

// [internal]
Expand All @@ -345,14 +341,14 @@ private template GenericReplace(args...)
alias tail = tuple[1 .. $];

static if (isSame!(from, head))
alias result = TypeTuple!(to, tail);
alias result = AliasSeq!(to, tail);
else
alias result = TypeTuple!(head,
alias result = AliasSeq!(head,
GenericReplace!(from, to, tail).result);
}
else
{
alias result = TypeTuple!();
alias result = AliasSeq!();
}
}

Expand Down Expand Up @@ -405,10 +401,10 @@ template ReplaceAll(alias T, alias U, TList...)
///
unittest
{
alias Types = TypeTuple!(int, long, long, int, float);
alias Types = AliasSeq!(int, long, long, int, float);

alias TL = ReplaceAll!(long, char, Types);
static assert(is(TL == TypeTuple!(int, char, char, int, float)));
static assert(is(TL == AliasSeq!(int, char, char, int, float)));
}

// [internal]
Expand All @@ -426,13 +422,13 @@ private template GenericReplaceAll(args...)
alias next = GenericReplaceAll!(from, to, tail).result;

static if (isSame!(from, head))
alias result = TypeTuple!(to, next);
alias result = AliasSeq!(to, next);
else
alias result = TypeTuple!(head, next);
alias result = AliasSeq!(head, next);
}
else
{
alias result = TypeTuple!();
alias result = AliasSeq!();
}
}

Expand Down Expand Up @@ -467,7 +463,7 @@ template Reverse(TList...)
else
{
alias Reverse =
TypeTuple!(
AliasSeq!(
Reverse!(TList[$/2 .. $ ]),
Reverse!(TList[ 0 .. $/2]));
}
Expand All @@ -476,10 +472,10 @@ template Reverse(TList...)
///
unittest
{
alias Types = TypeTuple!(int, long, long, int, float);
alias Types = AliasSeq!(int, long, long, int, float);

alias TL = Reverse!(Types);
static assert(is(TL == TypeTuple!(float, int, long, long, int)));
static assert(is(TL == AliasSeq!(float, int, long, long, int)));
}

/**
Expand All @@ -502,7 +498,7 @@ unittest
class A { }
class B : A { }
class C : B { }
alias Types = TypeTuple!(A, C, B);
alias Types = AliasSeq!(A, C, B);

MostDerived!(Object, Types) x; // x is declared as type C
static assert(is(typeof(x) == C));
Expand All @@ -518,7 +514,7 @@ template DerivedToFront(TList...)
alias DerivedToFront = TList;
else
alias DerivedToFront =
TypeTuple!(MostDerived!(TList[0], TList[1 .. $]),
AliasSeq!(MostDerived!(TList[0], TList[1 .. $]),
DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]),
TList[0],
TList[1 .. $])));
Expand All @@ -530,29 +526,29 @@ unittest
class A { }
class B : A { }
class C : B { }
alias Types = TypeTuple!(A, C, B);
alias Types = AliasSeq!(A, C, B);

alias TL = DerivedToFront!(Types);
static assert(is(TL == TypeTuple!(C, B, A)));
static assert(is(TL == AliasSeq!(C, B, A)));
}

/**
Evaluates to $(D TypeTuple!(F!(T[0]), F!(T[1]), ..., F!(T[$ - 1]))).
Evaluates to $(D AliasSeq!(F!(T[0]), F!(T[1]), ..., F!(T[$ - 1]))).
*/
template staticMap(alias F, T...)
{
static if (T.length == 0)
{
alias staticMap = TypeTuple!();
alias staticMap = AliasSeq!();
}
else static if (T.length == 1)
{
alias staticMap = TypeTuple!(F!(T[0]));
alias staticMap = AliasSeq!(F!(T[0]));
}
else
{
alias staticMap =
TypeTuple!(
AliasSeq!(
staticMap!(F, T[ 0 .. $/2]),
staticMap!(F, T[$/2 .. $ ]));
}
Expand All @@ -563,7 +559,7 @@ unittest
{
import std.traits : Unqual;
alias TL = staticMap!(Unqual, int, const int, immutable int);
static assert(is(TL == TypeTuple!(int, int, int)));
static assert(is(TL == AliasSeq!(int, int, int)));
}

unittest
Expand All @@ -576,10 +572,10 @@ unittest

// single
alias Single = staticMap!(Unqual, const int);
static assert(is(Single == TypeTuple!int));
static assert(is(Single == AliasSeq!int));

alias T = staticMap!(Unqual, int, const int, immutable int);
static assert(is(T == TypeTuple!(int, int, int)));
static assert(is(T == AliasSeq!(int, int, int)));
}

/**
Expand Down Expand Up @@ -652,26 +648,26 @@ unittest


/**
* Filters a $(D TypeTuple) using a template predicate. Returns a
* $(D TypeTuple) of the elements which satisfy the predicate.
* Filters an $(D AliasSeq) using a template predicate. Returns a
* $(D AliasSeq) of the elements which satisfy the predicate.
*/
template Filter(alias pred, TList...)
{
static if (TList.length == 0)
{
alias Filter = TypeTuple!();
alias Filter = AliasSeq!();
}
else static if (TList.length == 1)
{
static if (pred!(TList[0]))
alias Filter = TypeTuple!(TList[0]);
alias Filter = AliasSeq!(TList[0]);
else
alias Filter = TypeTuple!();
alias Filter = AliasSeq!();
}
else
{
alias Filter =
TypeTuple!(
AliasSeq!(
Filter!(pred, TList[ 0 .. $/2]),
Filter!(pred, TList[$/2 .. $ ]));
}
Expand All @@ -682,21 +678,21 @@ unittest
{
import std.traits : isNarrowString, isUnsigned;

alias Types1 = TypeTuple!(string, wstring, dchar[], char[], dstring, int);
alias Types1 = AliasSeq!(string, wstring, dchar[], char[], dstring, int);
alias TL1 = Filter!(isNarrowString, Types1);
static assert(is(TL1 == TypeTuple!(string, wstring, char[])));
static assert(is(TL1 == AliasSeq!(string, wstring, char[])));

alias Types2 = TypeTuple!(int, byte, ubyte, dstring, dchar, uint, ulong);
alias Types2 = AliasSeq!(int, byte, ubyte, dstring, dchar, uint, ulong);
alias TL2 = Filter!(isUnsigned, Types2);
static assert(is(TL2 == TypeTuple!(ubyte, uint, ulong)));
static assert(is(TL2 == AliasSeq!(ubyte, uint, ulong)));
}

unittest
{
import std.traits : isPointer;

static assert(is(Filter!(isPointer, int, void*, char[], int*) == TypeTuple!(void*, int*)));
static assert(is(Filter!isPointer == TypeTuple!()));
static assert(is(Filter!(isPointer, int, void*, char[], int*) == AliasSeq!(void*, int*)));
static assert(is(Filter!isPointer == AliasSeq!()));
}


Expand Down Expand Up @@ -740,7 +736,7 @@ unittest

unittest
{
foreach (T; TypeTuple!(int, staticMap, 42))
foreach (T; AliasSeq!(int, staticMap, 42))
{
static assert(!Instantiate!(templateNot!testAlways, T));
static assert(Instantiate!(templateNot!testNever, T));
Expand Down Expand Up @@ -791,7 +787,7 @@ unittest

unittest
{
foreach (T; TypeTuple!(int, staticMap, 42))
foreach (T; AliasSeq!(int, staticMap, 42))
{
static assert( Instantiate!(templateAnd!(), T));
static assert( Instantiate!(templateAnd!(testAlways), T));
Expand Down Expand Up @@ -849,7 +845,7 @@ unittest

unittest
{
foreach (T; TypeTuple!(int, staticMap, 42))
foreach (T; AliasSeq!(int, staticMap, 42))
{
static assert( Instantiate!(templateOr!(testAlways), T));
static assert( Instantiate!(templateOr!(testAlways, testAlways), T));
Expand Down Expand Up @@ -1022,7 +1018,7 @@ unittest
* Instantiates the given template with the given list of parameters.
*
* Used to work around syntactic limitations of D with regard to instantiating
* a template from a type tuple (e.g. T[0]!(...) is not valid) or a template
* a template from an alias sequence (e.g. T[0]!(...) is not valid) or a template
* returning another template (e.g. Foo!(Bar)!(Baz) is not allowed).
*/
// TODO: Consider publicly exposing this, maybe even if only for better
Expand Down
11 changes: 7 additions & 4 deletions std/net/curl.d
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,8 @@ void download(Conn = AutoProtocol)(const(char)[] url, string saveToPath, Conn co
{
import std.stdio : File;
conn.url = url;
auto f = File(saveToPath, "w");
conn.onReceive = (ubyte[] data) { f.write(data); return data.length; };
auto f = File(saveToPath, "wb");
conn.onReceive = (ubyte[] data) { f.rawWrite(data); return data.length; };
conn.perform();
}
else
Expand All @@ -309,8 +309,11 @@ unittest
if (!netAllowed()) return;
// No anonymous DigitalMars FTP access as of 2015
//download("ftp.digitalmars.com/sieve.ds", buildPath(tempDir(), "downloaded-ftp-file"));
download("d-lang.appspot.com/testUrl1", buildPath(tempDir(), "downloaded-http-file"));
download!(HTTP)("d-lang.appspot.com/testUrl1", buildPath(tempDir(), "downloaded-http-file"));
auto fn = buildPath(tempDir(), "downloaded-http-file");
download("d-lang.appspot.com/testUrl1", fn);
assert(std.file.readText(fn) == "Hello world\n");
download!(HTTP)("d-lang.appspot.com/testUrl1", fn);
assert(std.file.readText(fn) == "Hello world\n");
}

/** Upload file from local files system using the HTTP or FTP protocol.
Expand Down
400 changes: 200 additions & 200 deletions std/path.d

Large diffs are not rendered by default.

276 changes: 1 addition & 275 deletions std/range/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,14 @@ Submodules:
This module has two submodules:
$(LIST
$(DIV ,
The $(LINK2 std_range_primitives.html, $(D std._range.primitives)) submodule
provides basic _range functionality. It defines several templates for testing
whether a given object is a _range, what kind of _range it is, and provides
some common _range operations.
),
$(DIV ,
The $(LINK2 std_range_interfaces.html, $(D std._range.interfaces)) submodule
provides object-based interfaces for working with ranges via runtime
polymorphism.
))
The remainder of this module provides a rich set of _range creation and
composition templates that let you construct new ranges out of existing ranges:
Expand Down Expand Up @@ -81,10 +77,6 @@ $(BOOKTABLE ,
loop. Similar to $(D zip), except that $(D lockstep) is designed
especially for $(D foreach) loops.
))
$(TR $(TD $(D $(LREF merge)))
$(TD Creates a _range that iterates over the merged sorted union of its
sorted arguments. Is bidrectional if all its inputs are.
))
$(TR $(TD $(D $(LREF NullSink)))
$(TD An output _range that discards the data it receives.
))
Expand Down Expand Up @@ -8959,269 +8951,3 @@ if (is(typeof(fun) == void) || isSomeFunction!fun)

auto r = [1, 2, 3, 4].tee!func1.tee!func2;
}

package template CommonElementType(Rs...)
{
alias CommonElementType = CommonType!(staticMap!(ElementType, Rs));
}

package alias isSortedRange(R) = isInstanceOf!(SortedRange, R); // TODO Or use: __traits(isSame, TemplateOf!R, SortedRange)

/**
Merge several sorted ranges $(D rs) with less-than predicate function $(D
pred) into one single sorted range containing the sorted union of the
elements of inputs.
All of its inputs must be instantiations of $(XREF range, SortedRange). Use
the result of $(XREF algorithm, sort), or $(XREF range, assumeSorted) to
merge ranges known to be sorted (show in the example below). However, note
that there is currently no way of ensuring that two or more instances of
$(XREF range, SortedRange) are sorted using a specific comparison function
$(D pred). Therefore no checking is done here to assure that the $(D pred)
template parmeter matches the $(D pred) template parameter that were given to
each instance of $(XREF range, SortedRange) when the $(D rs) were
instantiated.
This algorithm is lazy, doing work progressively as elements are pulled off
the result.
Average complexity is $(BIGOH n * k) for $(D k) ranges of maximum length $(D n).
If all ranges have the same element type and offer it by $(D ref), merge
offers a range with mutable $(D front) (and $(D back) where appropriate) that
reflects in the original ranges.
If any of the inputs $(D rs) is infinite so is the merge result (`empty` is
statically always false).
Example:
-------
auto a = [0, 2, 4];
auto b = [1, 3, 5];
auto c = merge(a.assumeSorted, b.assumeSorted)
assert(equal(c, [0, 1, 2, 3, 4, 5]));
assert(equal(c.retro, [5, 4, 3, 2, 1, 0]));
-------
*/
auto merge(alias pred = "a < b", Rs...)(Rs rs)
if (Rs.length > 1 &&
allSatisfy!(isSortedRange,
staticMap!(Unqual, Rs)) &&
is(CommonElementType!(Rs)))
{
alias E = CommonElementType!Rs;
enum isBidirectional = allSatisfy!(isBidirectionalRange, staticMap!(Unqual, Rs));

struct Result
{
this(Rs source)
{
this.source = source;
this._lastFrontIndex = frontIndex;
static if (isBidirectional)
this._lastBackIndex = backIndex;
}

import std.typetuple : anySatisfy;
static if (anySatisfy!(isInfinite, Rs))
{
// Propagate infiniteness.
enum bool empty = false;
}
else
{
@property bool empty()
{
if (_lastFrontIndex == size_t.max)
return true;
static if (isBidirectional)
return _lastBackIndex == size_t.max;
else
return false;
}
}

@property auto ref front()
{
final switch (_lastFrontIndex)
{
foreach (i, _; Rs)
{
case i:
assert(!source[i].empty);
return source[i].front;
}
}
assert(0);
}

private size_t frontIndex()
{
size_t bestIndex = size_t.max; // indicate undefined
E bestElement;
foreach (i, _; Rs)
{
import std.functional : binaryFun;
if (!source[i].empty)
{
if (bestIndex == size_t.max || // either this is the first or
binaryFun!pred(source[i].front, bestElement))
{
bestIndex = i;
bestElement = source[i].front;
}
}
}
return bestIndex;
}

void popFront()
{
final switch (_lastFrontIndex)
{
foreach (i, _; Rs)
{
case i:
source[i].popFront();
break;
}
}
_lastFrontIndex = frontIndex;
}

static if (isBidirectional)
{
@property auto ref back()
{
final switch (_lastBackIndex)
{
foreach (i, _; Rs)
{
case i:
assert(!source[i].empty);
return source[i].back;
}
}
assert(0);
}

private size_t backIndex()
{
import std.functional : binaryFun;
size_t bestIndex = size_t.max; // indicate undefined
E bestElement;
foreach (i, _; Rs)
{
if (!source[i].empty)
{
if (bestIndex == size_t.max || // either this is the first or
binaryFun!pred(bestElement, source[i].back))
{
bestIndex = i;
bestElement = source[i].back;
}
}
}
return bestIndex;
}

void popBack()
{
final switch (_lastBackIndex)
{
foreach (i, _; Rs)
{
case i:
source[i].popBack();
break;
}
}
_lastBackIndex = backIndex;
}
}

static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs)))
{
@property auto save()
{
Result result = this;
foreach (i, _; Rs)
{
result.source[i] = result.source[i].save;
}
return result;
}
}

static if (allSatisfy!(hasLength, Rs))
{
@property size_t length()
{
size_t result;
foreach (i, _; Rs)
{
result += source[i].length;
}
return result;
}

alias opDollar = length;
}

public Rs source;
private size_t _lastFrontIndex = size_t.max;
static if (isBidirectional)
{
private size_t _lastBackIndex = size_t.max;
}
}

return Result(rs);
}

@safe pure nothrow unittest
{
import std.algorithm : equal;

alias S = short;
alias I = int;
alias D = double;

S[] a = [1, 2, 3];
I[] b = [50, 60];
D[] c = [10, 20, 30, 40];

auto d = ["a", "b", "c"];

static assert(!__traits(compiles, { auto m = merge(a.assumeSorted); }));
static assert(!__traits(compiles, { auto m = merge(a.assumeSorted,
d.assumeSorted); }));

auto m = merge(a.assumeSorted,
b.assumeSorted,
c.assumeSorted);

static assert(is(typeof(m.front) == CommonType!(S, I, D)));

assert(equal(m, [1, 2, 3, 10, 20, 30, 40, 50, 60]));
assert(equal(m.retro, [60, 50, 40, 30, 20, 10, 3, 2, 1]));

m.popFront;
assert(equal(m, [2, 3, 10, 20, 30, 40, 50, 60]));
m.popBack;
assert(equal(m, [2, 3, 10, 20, 30, 40, 50]));
m.popFront;
assert(equal(m, [3, 10, 20, 30, 40, 50]));
m.popBack;
assert(equal(m, [3, 10, 20, 30, 40]));
m.popFront;
assert(equal(m, [10, 20, 30, 40]));
m.popBack;
assert(equal(m, [10, 20, 30]));
m.popFront;
assert(equal(m, [20, 30]));
m.popBack;
assert(equal(m, [20]));
m.popFront;
assert(m.empty);
}
3 changes: 2 additions & 1 deletion std/string.d
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,8 @@ ptrdiff_t indexOf(T, size_t n)(ref T[n] s, in dchar c,
in CaseSensitive cs = CaseSensitive.yes) @safe pure
if (isSomeChar!T)
{
return indexOf(s[], c, cs);
auto r = s[];
return indexOf(r, c, cs);
}

@safe pure unittest
Expand Down
33 changes: 33 additions & 0 deletions std/traits.d
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
* $(LREF isFloatingPoint)
* $(LREF isIntegral)
* $(LREF isNarrowString)
* $(LREF isAutodecodableString)
* $(LREF isNumeric)
* $(LREF isPointer)
* $(LREF isScalarType)
Expand Down Expand Up @@ -5236,6 +5237,38 @@ unittest
}
}


/**
* Detect whether type $(D T) is a string that will be autodecoded.
*
* All arrays that use char, wchar, and their qualified versions are narrow
* strings. (Those include string and wstring).
* Aggregates that implicitly cast to narrow strings are included.
*
* Params:
* T = type to be tested
*
* Returns:
* true if T represents a string that is subject to autodecoding
*
* See Also:
* $(LREF isNarrowString)
*/
enum bool isAutodecodableString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isStaticArray!T;

///
unittest
{
static struct Stringish
{
string s;
alias s this;
}
assert(isAutodecodableString!wstring);
assert(isAutodecodableString!Stringish);
assert(!isAutodecodableString!dstring);
}

/**
* Detect whether type $(D T) is a static array.
*/
Expand Down
340 changes: 116 additions & 224 deletions std/typecons.d

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions std/typetuple.d
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,29 @@
module std.typetuple;

public import std.meta;

/**
* Alternate name for $(LREF AliasSeq) for legacy compatibility.
*/
alias TypeTuple = AliasSeq;

///
unittest
{
import std.typetuple;
alias TL = TypeTuple!(int, double);

int foo(TL td) // same as int foo(int, double);
{
return td[0] + cast(int)td[1];
}
}

///
unittest
{
alias TL = TypeTuple!(int, double);

alias Types = TypeTuple!(TL, char);
static assert(is(Types == TypeTuple!(int, double, char)));
}
52 changes: 26 additions & 26 deletions std/uni.d
Original file line number Diff line number Diff line change
Expand Up @@ -8312,7 +8312,7 @@ private auto toCaser(alias indexFn, uint maxIdx, alias tableFn, Range)(Range str
* $(LREF toUpper), $(LREF toLower)
*/

auto toLowerCase(Range)(Range str)
auto asLowerCase(Range)(Range str)
if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range))
{
static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
Expand All @@ -8329,7 +8329,7 @@ auto toLowerCase(Range)(Range str)
}

/// ditto
auto toUpperCase(Range)(Range str)
auto asUpperCase(Range)(Range str)
if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range))
{
static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
Expand All @@ -8350,14 +8350,14 @@ auto toUpperCase(Range)(Range str)
{
import std.algorithm: equal;

assert("hEllo".toUpperCase.equal("HELLO"));
assert("hEllo".asUpperCase.equal("HELLO"));
}

unittest
{
import std.array;

auto a = "HELLo".toLowerCase;
auto a = "HELLo".asLowerCase;
auto savea = a.save;
auto s = a.array;
assert(s == "hello");
Expand All @@ -8371,26 +8371,26 @@ unittest
{
import std.utf : byChar;

auto sx = slwr.toUpperCase.byChar.array;
auto sx = slwr.asUpperCase.byChar.array;
assert(sx == toUpper(slwr));
auto sy = upper[i].toLowerCase.byChar.array;
auto sy = upper[i].asLowerCase.byChar.array;
assert(sy == toLower(upper[i]));
}

// Not necessary to call r.front
for (auto r = lower[3].toUpperCase; !r.empty; r.popFront())
for (auto r = lower[3].asUpperCase; !r.empty; r.popFront())
{
}

import std.algorithm : equal;

"HELLo"w.toLowerCase.equal("hello"d);
"HELLo"w.toUpperCase.equal("HELLO"d);
"HELLo"d.toLowerCase.equal("hello"d);
"HELLo"d.toUpperCase.equal("HELLO"d);
"HELLo"w.asLowerCase.equal("hello"d);
"HELLo"w.asUpperCase.equal("HELLO"d);
"HELLo"d.asLowerCase.equal("hello"d);
"HELLo"d.asUpperCase.equal("HELLO"d);

import std.utf : byChar;
assert(toLower("\u1Fe2") == toLowerCase("\u1Fe2").byChar.array);
assert(toLower("\u1Fe2") == asLowerCase("\u1Fe2").byChar.array);
}

import std.stdio;
Expand Down Expand Up @@ -8457,7 +8457,7 @@ private auto toCapitalizer(alias indexFnUpper, uint maxIdxUpper, alias tableFnUp
if (!nLeft)
{
r.popFront();
lwr = r.toLowerCase();
lwr = r.asLowerCase();
lower = true;
}
}
Expand All @@ -8476,7 +8476,7 @@ private auto toCapitalizer(alias indexFnUpper, uint maxIdxUpper, alias tableFnUp

private:
Range r;
typeof(r.toLowerCase) lwr; // range representing the lower case rest of string
typeof(r.asLowerCase) lwr; // range representing the lower case rest of string
bool lower = false; // false for first character, true for rest of string
dchar[3] buf = void;
uint nLeft = 0;
Expand All @@ -8501,10 +8501,10 @@ private auto toCapitalizer(alias indexFnUpper, uint maxIdxUpper, alias tableFnUp
*
* See_Also:
* $(LREF toUpper), $(LREF toLower)
* $(LREF toUpperCase), $(LREF toLowerCase)
* $(LREF asUpperCase), $(LREF asLowerCase)
*/

auto toCapitalized(Range)(Range str)
auto asCapitalized(Range)(Range str)
if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range))
{
static if (ElementEncodingType!Range.sizeof < dchar.sizeof)
Expand All @@ -8525,20 +8525,20 @@ auto toCapitalized(Range)(Range str)
{
import std.algorithm: equal;

assert("hEllo".toCapitalized.equal("Hello"));
assert("hEllo".asCapitalized.equal("Hello"));
}

@safe pure nothrow @nogc unittest
{
auto r = "hEllo".toCapitalized();
auto r = "hEllo".asCapitalized();
assert(r.front == 'H');
}

unittest
{
import std.array;

auto a = "hELLo".toCapitalized;
auto a = "hELLo".asCapitalized;
auto savea = a.save;
auto s = a.array;
assert(s == "Hello");
Expand All @@ -8561,25 +8561,25 @@ unittest
{
import std.utf : byChar;

auto r = cases[i][0].toCapitalized.byChar.array;
auto r = cases[i][0].asCapitalized.byChar.array;
auto result = cases[i][1];
assert(r == result);
}

// Don't call r.front
for (auto r = "\u1Fe2".toCapitalized; !r.empty; r.popFront())
for (auto r = "\u1Fe2".asCapitalized; !r.empty; r.popFront())
{
}

import std.algorithm : equal;

"HELLo"w.toCapitalized.equal("Hello"d);
"hElLO"w.toCapitalized.equal("Hello"d);
"hello"d.toCapitalized.equal("Hello"d);
"HELLO"d.toCapitalized.equal("Hello"d);
"HELLo"w.asCapitalized.equal("Hello"d);
"hElLO"w.asCapitalized.equal("Hello"d);
"hello"d.asCapitalized.equal("Hello"d);
"HELLO"d.asCapitalized.equal("Hello"d);

import std.utf : byChar;
assert(toCapitalized("\u0130").byChar.array == toUpperCase("\u0130").byChar.array);
assert(asCapitalized("\u0130").byChar.array == asUpperCase("\u0130").byChar.array);
}

// TODO: helper, I wish std.utf was more flexible (and stright)
Expand Down
43 changes: 30 additions & 13 deletions std/utf.d
Original file line number Diff line number Diff line change
Expand Up @@ -3079,7 +3079,7 @@ enum dchar replacementDchar = '\uFFFD';
* input range
*/

auto byCodeUnit(R)(R r) if (isNarrowString!R)
auto byCodeUnit(R)(R r) if (isAutodecodableString!R)
{
/* Turn an array into an InputRange.
*/
Expand All @@ -3102,9 +3102,12 @@ auto byCodeUnit(R)(R r) if (isNarrowString!R)
r = r[0 .. $-1];
}

auto opSlice(size_t lower, size_t upper)
static if (!isAggregateType!R)
{
return ByCodeUnitImpl(r[lower..upper]);
auto opSlice(size_t lower, size_t upper)
{
return ByCodeUnitImpl(r[lower..upper]);
}
}

@property size_t length() const
Expand All @@ -3113,23 +3116,26 @@ auto byCodeUnit(R)(R r) if (isNarrowString!R)
}
alias opDollar = length;

@property auto save()
static if (!isAggregateType!R)
{
return ByCodeUnitImpl(r.save);
@property auto save()
{
return ByCodeUnitImpl(r.save);
}
}

private:
R r;
}

static assert(isRandomAccessRange!ByCodeUnitImpl);
static assert(isAggregateType!R || isRandomAccessRange!ByCodeUnitImpl);

return ByCodeUnitImpl(r);
}

/// Ditto
auto ref byCodeUnit(R)(R r)
if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
{
// byCodeUnit for ranges and dchar[] is a no-op
return r;
Expand Down Expand Up @@ -3204,6 +3210,17 @@ pure nothrow @nogc unittest
ret.popFront();
assert(ret.front == 'μ');
}
{
static struct Stringish
{
string s;
alias s this;
}

auto fn = Stringish("test.d");
auto x = fn.byCodeUnit();
assert(x.front == 't');
}
}

/****************************
Expand All @@ -3220,7 +3237,7 @@ pure nothrow @nogc unittest
* input range
*/

auto byChar(R)(R r) if (isNarrowString!R)
auto byChar(R)(R r) if (isAutodecodableString!R)
{
/* This and the following two serve as adapters to convert arrays to ranges,
* so the following three
Expand All @@ -3239,7 +3256,7 @@ auto byChar(R)(R r) if (isNarrowString!R)
}

/// Ditto
auto byWchar(R)(R r) if (isNarrowString!R)
auto byWchar(R)(R r) if (isAutodecodableString!R)
{
alias tchar = Unqual!(ElementEncodingType!R);

Expand All @@ -3254,7 +3271,7 @@ auto byWchar(R)(R r) if (isNarrowString!R)
}

/// Ditto
auto byDchar(R)(R r) if (isNarrowString!R)
auto byDchar(R)(R r) if (isAutodecodableString!R)
{
alias tchar = Unqual!(ElementEncodingType!R);

Expand All @@ -3264,7 +3281,7 @@ auto byDchar(R)(R r) if (isNarrowString!R)

/// Ditto
auto ref byChar(R)(R r)
if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
{
alias tchar = Unqual!(ElementEncodingType!R);

Expand Down Expand Up @@ -3427,7 +3444,7 @@ pure nothrow @nogc unittest

/// Ditto
auto ref byWchar(R)(R r)
if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
{
alias tchar = Unqual!(ElementEncodingType!R);

Expand Down Expand Up @@ -3564,7 +3581,7 @@ pure nothrow @nogc unittest

/// Ditto
auto ref byDchar(R)(R r)
if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
if (!isAutodecodableString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R))
{
alias tchar = Unqual!(ElementEncodingType!R);

Expand Down