/** This module contains compiler support for switch...case statements Copyright: Copyright Digital Mars 2000 - 2019. License: Distributed under the $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). (See accompanying file LICENSE) Source: $(DRUNTIMESRC core/internal/_switch_.d) */ module core.internal.switch_; /** Support for switch statements switching on strings. Params: caseLabels = sorted array of strings generated by compiler. Note the strings are sorted by length first, and then lexicographically. condition = string to look up in table Returns: index of match in caseLabels, a negative integer if not found */ int __switch(T, caseLabels...)(/*in*/ const scope T[] condition) pure nothrow @safe @nogc { // This closes recursion for other cases. static if (caseLabels.length == 0) { return int.min; } else static if (caseLabels.length == 1) { return __cmp(condition, caseLabels[0]) == 0 ? 0 : int.min; } // To be adjusted after measurements // Compile-time inlined binary search. else static if (caseLabels.length < 7) { int r = void; enum mid = cast(int)caseLabels.length / 2; if (condition.length == caseLabels[mid].length) { r = __cmp(condition, caseLabels[mid]); if (r == 0) return mid; } else { // Equivalent to (but faster than) condition.length > caseLabels[$ / 2].length ? 1 : -1 r = ((condition.length > caseLabels[mid].length) << 1) - 1; } if (r < 0) { // Search the left side return __switch!(T, caseLabels[0 .. mid])(condition); } else { // Search the right side return __switch!(T, caseLabels[mid + 1 .. $])(condition) + mid + 1; } } else { // Need immutable array to be accessible in pure code, but case labels are // currently coerced to the switch condition type (e.g. const(char)[]). pure @trusted nothrow @nogc asImmutable(scope const(T[])[] items) { assert(__ctfe); // only @safe for CTFE immutable T[][caseLabels.length] result = cast(immutable)(items[]); return result; } static immutable T[][caseLabels.length] cases = asImmutable([caseLabels]); // Run-time binary search in a static array of labels. return __switchSearch!T(cases[], condition); } } // binary search in sorted string cases, also see `__switch`. private int __switchSearch(T)(/*in*/ const scope T[][] cases, /*in*/ const scope T[] condition) pure nothrow @safe @nogc { size_t low = 0; size_t high = cases.length; do { auto mid = (low + high) / 2; int r = void; if (condition.length == cases[mid].length) { r = __cmp(condition, cases[mid]); if (r == 0) return cast(int) mid; } else { // Generates better code than "expr ? 1 : -1" on dmd and gdc, same with ldc r = ((condition.length > cases[mid].length) << 1) - 1; } if (r > 0) low = mid + 1; else high = mid; } while (low < high); // Not found return -1; } @system unittest { static void testSwitch(T)() { switch (cast(T[]) "c") { case "coo": default: break; } static int bug5381(immutable(T)[] s) { switch (s) { case "unittest": return 1; case "D_Version2": return 2; case "nonenone": return 3; case "none": return 4; case "all": return 5; default: return 6; } } int rc = bug5381("unittest"); assert(rc == 1); rc = bug5381("D_Version2"); assert(rc == 2); rc = bug5381("nonenone"); assert(rc == 3); rc = bug5381("none"); assert(rc == 4); rc = bug5381("all"); assert(rc == 5); rc = bug5381("nonerandom"); assert(rc == 6); static int binarySearch(immutable(T)[] s) { switch (s) { static foreach (i; 0 .. 16) case i.stringof: return i; default: return -1; } } static foreach (i; 0 .. 16) assert(binarySearch(i.stringof) == i); assert(binarySearch("") == -1); assert(binarySearch("sth.") == -1); assert(binarySearch(null) == -1); static int bug16739(immutable(T)[] s) { switch (s) { case "\u0100": return 1; case "a": return 2; default: return 3; } } assert(bug16739("\u0100") == 1); assert(bug16739("a") == 2); assert(bug16739("foo") == 3); } testSwitch!char; testSwitch!wchar; testSwitch!dchar; } /** Compiler lowers final switch default case to this (which is a runtime error) Old implementation is in core/exception.d */ void __switch_error()(string file = __FILE__, size_t line = __LINE__) { import core.exception : __switch_errorT; __switch_errorT(file, line); }