From fa5830c32ee08c34bdfc10bfb62dd7cb5ee87ba3 Mon Sep 17 00:00:00 2001 From: Jack Stouffer Date: Mon, 4 Jun 2018 15:31:06 -0400 Subject: [PATCH] Fix Issue 18948 - toLower and toUpper should work with random access ranges --- std/uni.d | 85 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/std/uni.d b/std/uni.d index 35d5f014211..a7d937d73ca 100644 --- a/std/uni.d +++ b/std/uni.d @@ -8997,20 +8997,24 @@ private alias UpperTriple = AliasSeq!(toUpperIndex, MAX_SIMPLE_UPPER, toUpperTab private alias LowerTriple = AliasSeq!(toLowerIndex, MAX_SIMPLE_LOWER, toLowerTab); // generic toUpper/toLower on whole string, creates new or returns as is -private S toCase(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, S)(S s) @trusted pure -if (isSomeString!S) +private ElementEncodingType!S[] toCase(alias indexFn, uint maxIdx, alias tableFn, alias asciiConvert, S)(S s) +if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) { - import std.array : appender; + import std.array : appender, array; import std.ascii : isASCII; + import std.utf : byDchar; - foreach (i, dchar cOuter; s) + auto r = s.byDchar; + for (size_t i; !r.empty; ++i, r.popFront()) { + auto cOuter = r.front; ushort idx = indexFn(cOuter); if (idx == ushort.max) continue; - auto result = appender!S(s[0 .. i]); + auto result = appender!(ElementEncodingType!S[])(); + result.put(s[0 .. i]); result.reserve(s.length); - foreach (dchar c; s[i .. $]) + foreach (dchar c; s[i .. $].byDchar) { if (c.isASCII) { @@ -9039,7 +9043,11 @@ if (isSomeString!S) } return result.data; } - return s; + + static if (isSomeString!S) + return s; + else + return s.array; } @safe unittest //12428 @@ -9783,16 +9791,28 @@ dchar toLower(dchar c) } /++ - Returns a string which is identical to `s` except that all of its + Creates a new array which is identical to `s` except that all of its characters are converted to lowercase (by preforming Unicode lowercase mapping). - If none of `s` characters were affected, then `s` itself is returned. + If none of `s` characters were affected, then `s` itself is returned if `s` is a + `string`-like type. + + Params: + s = A $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives) + of characters + Returns: + An array with the same element type as `s`. +/ -S toLower(S)(S s) @trusted pure -if (isSomeString!S) +ElementEncodingType!S[] toLower(S)(S s) +if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) { static import std.ascii; - return toCase!(LowerTriple, std.ascii.toLower)(s); + + static if (isSomeString!S) + return () @trusted { return toCase!(LowerTriple, std.ascii.toLower)(s); } (); + else + return toCase!(LowerTriple, std.ascii.toLower)(s); } + // overloads for the most common cases to reduce compile time @safe pure /*TODO nothrow*/ { @@ -9900,6 +9920,15 @@ if (isSomeString!S) assert(toUpper(c) == '\u1F8F'); } +@safe pure unittest +{ + import std.algorithm.comparison : cmp, equal; + import std.utf : byCodeUnit; + auto r1 = "FoL".byCodeUnit; + assert(r1.toLower.cmp("fol") == 0); + auto r2 = "A\u0460B\u0461d".byCodeUnit; + assert(r2.toLower.cmp("a\u0461b\u0461d") == 0); +} /++ If `c` is a Unicode lowercase $(CHARACTER), then its uppercase equivalent @@ -9965,16 +9994,28 @@ dchar toUpper(dchar c) } /++ - Returns a string which is identical to `s` except that all of its + Allocates a new array which is identical to `s` except that all of its characters are converted to uppercase (by preforming Unicode uppercase mapping). - If none of `s` characters were affected, then `s` itself is returned. + If none of `s` characters were affected, then `s` itself is returned if `s` + is a `string`-like type. + + Params: + s = A $(REF_ALTTEXT random access range, isRandomAccessRange, std,range,primitives) + of characters + Returns: + An new array with the same element type as `s`. +/ -S toUpper(S)(S s) @trusted pure -if (isSomeString!S) +ElementEncodingType!S[] toUpper(S)(S s) +if (isSomeString!S || (isRandomAccessRange!S && hasLength!S && hasSlicing!S && isSomeChar!(ElementType!S))) { static import std.ascii; - return toCase!(UpperTriple, std.ascii.toUpper)(s); + + static if (isSomeString!S) + return () @trusted { return toCase!(UpperTriple, std.ascii.toUpper)(s); } (); + else + return toCase!(UpperTriple, std.ascii.toUpper)(s); } + // overloads for the most common cases to reduce compile time @safe pure /*TODO nothrow*/ { @@ -10088,6 +10129,16 @@ if (isSomeString!S) }} } +// test random access ranges +@safe pure unittest +{ + import std.algorithm.comparison : cmp; + import std.utf : byCodeUnit; + auto s1 = "FoL".byCodeUnit; + assert(s1.toUpper.cmp("FOL") == 0); + auto s2 = "a\u0460B\u0461d".byCodeUnit; + assert(s2.toUpper.cmp("A\u0460B\u0460D") == 0); +} /++ Returns whether `c` is a Unicode alphabetic $(CHARACTER)