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);
}
|