diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-01-07 01:05:54 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2025-01-11 02:21:39 +0100 |
commit | dd3026f05111a0858ee87146ba9c37f164afa815 (patch) | |
tree | 98325195cedbde6e098d1994ae97975e1461ae21 /libphobos/src/std/uni | |
parent | b0eeb540497c7b9dee01f8724f9a4978b53a12ae (diff) | |
download | gcc-dd3026f05111a0858ee87146ba9c37f164afa815.zip gcc-dd3026f05111a0858ee87146ba9c37f164afa815.tar.gz gcc-dd3026f05111a0858ee87146ba9c37f164afa815.tar.bz2 |
d: Merge dmd, druntime 2b89c2909d, phobos bdedad3bf
D front-end changes:
- Import latest fixes from dmd v2.110.0-beta.1.
D runtime changes:
- Import latest fixes from druntime v2.110.0-beta.1.
Phobos changes:
- Import latest fixes from phobos v2.110.0-beta.1.
- Added `popGrapheme' function to `std.uni'.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd 2b89c2909d.
* Make-lang.in (D_FRONTEND_OBJS): Rename d/basicmangle.o to
d/mangle-basic.o, d/cppmangle.o to d/mangle-cpp.o, and d/dmangle.o to
d/mangle-package.o.
(d/mangle-%.o): New rule.
* d-builtins.cc (maybe_set_builtin_1): Update for new front-end
interface.
* d-diagnostic.cc (verrorReport): Likewise.
(verrorReportSupplemental): Likewise.
* d-frontend.cc (getTypeInfoType): Likewise.
* d-lang.cc (d_init_options): Likewise.
(d_handle_option): Likewise.
(d_post_options): Likewise.
* d-target.cc (TargetC::contributesToAggregateAlignment): New.
* d-tree.h (create_typeinfo): Adjust prototype.
* decl.cc (layout_struct_initializer): Update for new front-end
interface.
* typeinfo.cc (create_typeinfo): Remove generate parameter.
* types.cc (layout_aggregate_members): Update for new front-end
interface.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime 2b89c2909d.
* src/MERGE: Merge upstream phobos bdedad3bf.
Diffstat (limited to 'libphobos/src/std/uni')
-rw-r--r-- | libphobos/src/std/uni/package.d | 250 |
1 files changed, 165 insertions, 85 deletions
diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d index f7610c0..34d15e0 100644 --- a/libphobos/src/std/uni/package.d +++ b/libphobos/src/std/uni/package.d @@ -16,6 +16,7 @@ $(TR $(TD Decode) $(TD $(LREF byGrapheme) $(LREF decodeGrapheme) $(LREF graphemeStride) + $(LREF popGrapheme) )) $(TR $(TD Comparison) $(TD $(LREF icmp) @@ -708,8 +709,8 @@ import std.meta : AliasSeq; import std.range.primitives : back, ElementEncodingType, ElementType, empty, front, hasLength, hasSlicing, isForwardRange, isInputRange, isRandomAccessRange, popFront, put, save; -import std.traits : isConvertibleToString, isIntegral, isSomeChar, - isSomeString, Unqual, isDynamicArray; +import std.traits : isAutodecodableString, isConvertibleToString, isIntegral, + isSomeChar, isSomeString, Unqual, isDynamicArray; // debug = std_uni; import std.internal.unicode_tables; // generated file @@ -961,7 +962,7 @@ struct MultiArray(Types...) } void store(OutRange)(scope OutRange sink) const - if (isOutputRange!(OutRange, char)) + if (isOutputRange!(OutRange, char)) { import std.format.write : formattedWrite; formattedWrite(sink, "[%( 0x%x, %)]", offsets[]); @@ -1652,7 +1653,7 @@ if (is(T : ElementType!Range)) template sharMethod(alias uniLowerBound) { size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle) - if (is(T : ElementType!Range)) + if (is(T : ElementType!Range)) { import std.functional : binaryFun; import std.math.algebraic : nextPow2, truncPow2; @@ -1768,19 +1769,19 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (!isInputRange!V) + if (!isInputRange!V) { arr ~= force!T(value); } static void append(T, V)(ref T[] arr, V value) - if (isInputRange!V) + if (isInputRange!V) { insertInPlace(arr, arr.length, value); } static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000 - if (isDynamicArray!T && is(Unqual!T == T)) + if (isDynamicArray!T && is(Unqual!T == T)) { debug { @@ -1790,7 +1791,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void destroy(T)(ref T arr) pure // pure required for -dip25, inferred for -dip1000 - if (isDynamicArray!T && !is(Unqual!T == T)) + if (isDynamicArray!T && !is(Unqual!T == T)) { arr = null; } @@ -1845,7 +1846,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (!isInputRange!V) + if (!isInputRange!V) { if (arr.length == size_t.max) assert(0); arr = realloc(arr, arr.length+1); @@ -1862,7 +1863,7 @@ alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound; } static void append(T, V)(ref T[] arr, V value) - if (isInputRange!V && hasLength!V) + if (isInputRange!V && hasLength!V) { import core.checkedint : addu; bool overflow; @@ -2058,7 +2059,7 @@ public struct InversionList(SP=GcPolicy) Construct from another code point set of any type. */ this(Set)(Set set) pure - if (isCodepointSet!Set) + if (isCodepointSet!Set) { uint[] arr; foreach (v; set.byInterval) @@ -2073,7 +2074,7 @@ public struct InversionList(SP=GcPolicy) Construct a set from a forward range of code point intervals. */ this(Range)(Range intervals) pure - if (isForwardRange!Range && isIntegralPair!(ElementType!Range)) + if (isForwardRange!Range && isIntegralPair!(ElementType!Range)) { uint[] arr; foreach (v; intervals) @@ -2245,7 +2246,7 @@ public: ) */ This opBinary(string op, U)(U rhs) - if (isCodepointSet!U || is(U:dchar)) + if (isCodepointSet!U || is(U:dchar)) { static if (op == "&" || op == "|" || op == "~") {// symmetric ops thus can swap arguments to reuse r-value @@ -2310,7 +2311,7 @@ public: /// The 'op=' versions of the above overloaded operators. ref This opOpAssign(string op, U)(U rhs) - if (isCodepointSet!U || is(U:dchar)) + if (isCodepointSet!U || is(U:dchar)) { static if (op == "|") // union { @@ -2342,7 +2343,7 @@ public: the same as $(LREF opIndex). */ bool opBinaryRight(string op: "in", U)(U ch) const - if (is(U : dchar)) + if (is(U : dchar)) { return this[ch]; } @@ -2522,7 +2523,7 @@ private: package(std) // used from: std.regex.internal.parser ref intersect(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker mark; foreach ( i; rhs.byInterval) @@ -2556,7 +2557,7 @@ private: // same as the above except that skip & drop parts are swapped package(std) // used from: std.regex.internal.parser ref sub(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker mark; foreach (i; rhs.byInterval) @@ -2569,7 +2570,7 @@ private: package(std) // used from: std.regex.internal.parse ref add(U)(U rhs) - if (isCodepointSet!U) + if (isCodepointSet!U) { Marker start; foreach (i; rhs.byInterval) @@ -3206,7 +3207,7 @@ struct CowArray(SP=GcPolicy) } this(Range)(Range range) - if (isInputRange!Range && hasLength!Range) + if (isInputRange!Range && hasLength!Range) { import std.algorithm.mutation : copy; length = range.length; @@ -3214,7 +3215,7 @@ struct CowArray(SP=GcPolicy) } this(Range)(Range range) - if (isForwardRange!Range && !hasLength!Range) + if (isForwardRange!Range && !hasLength!Range) { import std.algorithm.mutation : copy; import std.range.primitives : walkLength; @@ -3336,7 +3337,7 @@ struct CowArray(SP=GcPolicy) } void append(Range)(Range range) - if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) + if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint)) { size_t nl = length + range.length; length = nl; @@ -3793,7 +3794,7 @@ auto arrayRepr(T)(T x) template mapTrieIndex(Prefix...) { size_t mapTrieIndex(Key)(Key key) - if (isValidPrefixForTrie!(Key, Prefix)) + if (isValidPrefixForTrie!(Key, Prefix)) { alias p = Prefix; size_t idx; @@ -4184,7 +4185,7 @@ if (isValidPrefixForTrie!(Key, Args) /// void store(OutRange)(scope OutRange sink) const - if (isOutputRange!(OutRange, char)) + if (isOutputRange!(OutRange, char)) { _table.store(sink); } @@ -4285,7 +4286,7 @@ public template codepointSetTrie(sizes...) if (sumOfIntegerTuple!sizes == 21) { auto codepointSetTrie(Set)(Set set) - if (isCodepointSet!Set) + if (isCodepointSet!Set) { auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false); foreach (ival; set.byInterval) @@ -4322,7 +4323,7 @@ if (sumOfIntegerTuple!sizes == 21) static if (is(TypeOfBitPacked!T == bool)) { auto codepointTrie(Set)(const scope Set set) - if (isCodepointSet!Set) + if (isCodepointSet!Set) { return codepointSetTrie(set); } @@ -4337,9 +4338,9 @@ if (sumOfIntegerTuple!sizes == 21) // unsorted range of pairs /// auto codepointTrie(R)(R range, T defValue=T.init) - if (isInputRange!R - && is(typeof(ElementType!R.init[0]) : T) - && is(typeof(ElementType!R.init[1]) : dchar)) + if (isInputRange!R + && is(typeof(ElementType!R.init[0]) : T) + && is(typeof(ElementType!R.init[1]) : dchar)) { // build from unsorted array of pairs // TODO: expose index sorting functions for Trie @@ -4467,8 +4468,8 @@ if (isValidArgsForTrie!(Key, Args)) $(REF setUnion, std,_algorithm). */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) - && is(typeof(Range.init.front[1]) : Key)) + if (isInputRange!Range && is(typeof(Range.init.front[0]) : Value) + && is(typeof(Range.init.front[1]) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (v; range) @@ -4487,9 +4488,9 @@ if (isValidArgsForTrie!(Key, Args)) and `filler` is false. */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (is(TypeOfBitPacked!Value == bool) - && isInputRange!Range && is(typeof(Range.init.front[0]) : Key) - && is(typeof(Range.init.front[1]) : Key)) + if (is(TypeOfBitPacked!Value == bool) + && isInputRange!Range && is(typeof(Range.init.front[0]) : Key) + && is(typeof(Range.init.front[1]) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (ival; range) @@ -4498,9 +4499,9 @@ if (isValidArgsForTrie!(Key, Args)) } auto buildTrie(Range)(Range range, Value filler, bool unsorted) - if (isInputRange!Range - && is(typeof(Range.init.front[0]) : Value) - && is(typeof(Range.init.front[1]) : Key)) + if (isInputRange!Range + && is(typeof(Range.init.front[0]) : Value) + && is(typeof(Range.init.front[1]) : Key)) { import std.algorithm.sorting : multiSort; alias Comps = GetComparators!(Prefix.length); @@ -4519,8 +4520,8 @@ if (isValidArgsForTrie!(Key, Args)) If no filler provided keys map to true, and `filler` is false. */ auto buildTrie(Range)(Range range, Value filler=Value.init) - if (is(TypeOfBitPacked!Value == bool) - && isInputRange!Range && is(typeof(Range.init.front) : Key)) + if (is(TypeOfBitPacked!Value == bool) + && isInputRange!Range && is(typeof(Range.init.front) : Key)) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (v; range) @@ -4533,7 +4534,7 @@ if (isValidArgsForTrie!(Key, Args)) of values where array index serves as key. */ auto buildTrie()(Value[] array, Value filler=Value.init) - if (isUnsigned!Key) + if (isUnsigned!Key) { auto builder = TrieBuilder!(Value, Key, Prefix)(filler); foreach (idx, v; array) @@ -4595,21 +4596,21 @@ public struct MatcherConcept of the result of test.) */ public bool match(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } ///ditto public bool skip(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } ///ditto public bool test(Range)(ref Range inp) - if (isRandomAccessRange!Range && is(ElementType!Range : char)) + if (isRandomAccessRange!Range && is(ElementType!Range : char)) { assert(false); } @@ -4765,7 +4766,7 @@ template Utf8Matcher() } static auto encode(size_t sz)(dchar ch) - if (sz > 1) + if (sz > 1) { import std.utf : encodeUTF = encode; char[4] buf; @@ -4821,8 +4822,8 @@ template Utf8Matcher() enum dispatch = genDispatch(); public bool match(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.skipOnMatch; assert(!inp.empty); @@ -4846,8 +4847,8 @@ template Utf8Matcher() static if (Sizes.length == 4) // can skip iff can detect all encodings { public bool skip(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.alwaysSkip; assert(!inp.empty); @@ -4868,8 +4869,8 @@ template Utf8Matcher() } public bool test(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : char) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : char) && + !isDynamicArray!Range) { enum mode = Mode.neverSkip; assert(!inp.empty); @@ -4887,19 +4888,19 @@ template Utf8Matcher() } bool match(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"match"(str); } bool skip(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"skip"(str); } bool test(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"test"(str); } @@ -5056,8 +5057,8 @@ template Utf16Matcher() mixin template DefMatcher() { public bool match(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.skipOnMatch; assert(!inp.empty); @@ -5083,8 +5084,8 @@ template Utf16Matcher() static if (Sizes.length == 2) { public bool skip(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.alwaysSkip; assert(!inp.empty); @@ -5105,8 +5106,8 @@ template Utf16Matcher() } public bool test(Range)(ref Range inp) const - if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && - !isDynamicArray!Range) + if (isRandomAccessRange!Range && is(ElementType!Range : wchar) && + !isDynamicArray!Range) { enum mode = Mode.neverSkip; assert(!inp.empty); @@ -5118,19 +5119,19 @@ template Utf16Matcher() } bool match(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"match"(str); } bool skip(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"skip"(str); } bool test(C)(ref C[] str) const - if (isSomeChar!C) + if (isSomeChar!C) { return fwdStr!"test"(str); } @@ -5139,7 +5140,7 @@ template Utf16Matcher() } struct Impl(Sizes...) - if (Sizes.length >= 1 && Sizes.length <= 2) + if (Sizes.length >= 1 && Sizes.length <= 2) { private: import std.meta : allSatisfy; @@ -5229,7 +5230,7 @@ template Utf16Matcher() } struct CherryPick(I, Sizes...) - if (Sizes.length >= 1 && Sizes.length <= 2) + if (Sizes.length >= 1 && Sizes.length <= 2) { private: import std.meta : allSatisfy; @@ -6105,7 +6106,7 @@ template SetSearcher(alias table, string kind) { /// Run-time checked search. static auto opCall(C)(const scope C[] name) - if (is(C : dchar)) + if (is(C : dchar)) { import std.conv : to; CodepointSet set; @@ -6765,7 +6766,7 @@ struct UnicodeSetParser(Range) sets. */ static auto opCall(C)(const scope C[] name) - if (is(C : dchar)) + if (is(C : dchar)) { return loadAny(name); } @@ -7148,17 +7149,25 @@ private immutable TransformRes TransformRes.goOn ]; -template genericDecodeGrapheme(bool getValue) -{ - static if (getValue) +enum GraphemeRet { none, step, value } + +template genericDecodeGrapheme(GraphemeRet retType) +{ alias Ret = GraphemeRet; + + static if (retType == Ret.value) alias Value = Grapheme; - else + else static if (retType == Ret.step) + alias Value = size_t; + else static if (retType == Ret.none) alias Value = void; Value genericDecodeGrapheme(Input)(ref Input range) { - static if (getValue) - Grapheme grapheme; + static if (retType == Ret.value) + Grapheme result; + else static if (retType == Ret.step) + size_t result = 0; + auto state = GraphemeState.Start; dchar ch; @@ -7173,8 +7182,10 @@ template genericDecodeGrapheme(bool getValue) with(TransformRes) { case goOn: - static if (getValue) - grapheme ~= ch; + static if (retType == Ret.value) + result ~= ch; + else static if (retType == Ret.step) + result++; range.popFront(); continue; @@ -7182,8 +7193,10 @@ template genericDecodeGrapheme(bool getValue) goto rerun; case retInclude: - static if (getValue) - grapheme ~= ch; + static if (retType == Ret.value) + result ~= ch; + else static if (retType == Ret.step) + result++; range.popFront(); break outer; @@ -7192,8 +7205,8 @@ template genericDecodeGrapheme(bool getValue) } } - static if (getValue) - return grapheme; + static if (retType != Ret.none) + return result; } } @@ -7217,7 +7230,7 @@ if (is(C : dchar)) { auto src = input[index..$]; auto n = src.length; - genericDecodeGrapheme!(false)(src); + genericDecodeGrapheme!(GraphemeRet.none)(src); return n - src.length; } @@ -7279,7 +7292,7 @@ if (is(C : dchar)) Grapheme decodeGrapheme(Input)(ref Input inp) if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) { - return genericDecodeGrapheme!true(inp); + return genericDecodeGrapheme!(GraphemeRet.value)(inp); } @safe unittest @@ -7305,6 +7318,73 @@ if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) } /++ + Reads one full grapheme cluster from an + $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of dchar `inp`, + but doesn't return it. Instead returns the number of code units read. + This differs from number of code points read only if `input` is an + autodecodable string. + + Note: + This function modifies `inp` and thus `inp` + must be an L-value. ++/ +size_t popGrapheme(Input)(ref Input inp) +if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) +{ + static if (isAutodecodableString!Input || hasLength!Input) + { + // Why count each step in the decoder when you can just + // measure the grapheme in one go? + auto n = inp.length; + genericDecodeGrapheme!(GraphemeRet.none)(inp); + return n - inp.length; + } + else return genericDecodeGrapheme!(GraphemeRet.step)(inp); +} + +/// +@safe pure unittest +{ + // Two Union Jacks of the Great Britain in each + string s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + wstring ws = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + dstring ds = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + + // String pop length in code units, not points. + assert(s.popGrapheme() == 8); + assert(ws.popGrapheme() == 4); + assert(ds.popGrapheme() == 2); + + assert(s == "\U0001F1EC\U0001F1E7"); + assert(ws == "\U0001F1EC\U0001F1E7"); + assert(ds == "\U0001F1EC\U0001F1E7"); + + import std.algorithm.comparison : equal; + import std.algorithm.iteration : filter; + + // Also works for non-random access ranges as long as the + // character type is 32-bit. + auto testPiece = "\r\nhello!"d.filter!(x => !x.isAlpha); + // Windows-style line ending is two code points in a single grapheme. + assert(testPiece.popGrapheme() == 2); + assert(testPiece.equal("!"d)); +} + +// Attribute compliance test. Should be nothrow `@nogc` when +// no autodecoding needed. +@safe pure nothrow @nogc unittest +{ + import std.algorithm.iteration : filter; + + auto str = "abcdef"d; + assert(str.popGrapheme() == 1); + + // also test with non-random access + auto filtered = "abcdef"d.filter!(x => x%2); + assert(filtered.popGrapheme() == 1); +} + +/++ $(P Iterate a string by $(LREF Grapheme).) $(P Useful for doing string manipulation that needs to be aware @@ -7556,15 +7636,15 @@ if (isInputRange!Range && is(immutable ElementType!Range == immutable dchar)) public: /// Ctor this(C)(const scope C[] chars...) - if (is(C : dchar)) + if (is(C : dchar)) { this ~= chars; } ///ditto this(Input)(Input seq) - if (!isDynamicArray!Input - && isInputRange!Input && is(ElementType!Input : dchar)) + if (!isDynamicArray!Input + && isInputRange!Input && is(ElementType!Input : dchar)) { this ~= seq; } @@ -7683,7 +7763,7 @@ public: /// Append all $(CHARACTERS) from the input range `inp` to this Grapheme. ref opOpAssign(string op, Input)(scope Input inp) - if (isInputRange!Input && is(ElementType!Input : dchar)) + if (isInputRange!Input && is(ElementType!Input : dchar)) { static if (op == "~") { @@ -7722,7 +7802,7 @@ public: @property bool valid()() /*const*/ { auto r = this[]; - genericDecodeGrapheme!false(r); + genericDecodeGrapheme!(GraphemeRet.none)(r); return r.length == 0; } @@ -9868,7 +9948,7 @@ private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn) { void toCaseInPlaceAlloc(C)(ref C[] s, size_t curIdx, size_t destIdx) @trusted pure - if (is(C == char) || is(C == wchar) || is(C == dchar)) + if (is(C == char) || is(C == wchar) || is(C == dchar)) { import std.utf : decode; alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn); |