1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
/**
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);
}
|