1 /++ 2 Enum utilities. 3 4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 Authors: Ilya Yaroshenko 6 Macros: 7 +/ 8 module mir.enums; 9 10 private bool hasSeqGrow(T)(T[] elems) 11 if (__traits(isIntegral, T)) 12 { 13 assert(elems.length); 14 auto min = elems[0]; 15 foreach (i, e; elems) 16 if (i != e - min) 17 return false; 18 return true; 19 } 20 21 /++ 22 Enum index that corresponds of the list returned by `std.traits.EnumMembers`. 23 Returns: 24 enum member position index in the enum definition that corresponds the `value`. 25 +/ 26 bool getEnumIndex(T)(const T value, ref uint index) 27 @safe pure nothrow @nogc 28 if (is(T == enum)) 29 { 30 import std.traits: EnumMembers, isSomeString; 31 import mir.utility: _expect; 32 33 static if (__traits(isFloating, T)) 34 { 35 // TODO: index based binary searach 36 foreach (i, member; enumMembers!T) 37 { 38 if (value == member) 39 { 40 index = cast(uint) i; 41 return true; 42 } 43 } 44 return false; 45 } 46 else 47 static if (!__traits(isIntegral, T)) //strings 48 { 49 enum string[1] stringEnumValue(alias symbol) = [symbol]; 50 return getEnumIndexFromKey!(T, false, stringEnumValue)(value, index); 51 } 52 else 53 static if (hasSeqGrow(enumMembers!T)) 54 { 55 import std.traits: Unsigned; 56 const shifted = cast(Unsigned!(typeof(value - T.min)))(value - T.min); 57 if (_expect(shifted < enumMembers!T.length, true)) 58 { 59 index = cast(uint) shifted; 60 return true; 61 } 62 return false; 63 } 64 else 65 static if (is(T : bool)) 66 { 67 index = !value; 68 return true; 69 } 70 else 71 { 72 import std.traits: Unsigned; 73 alias U = Unsigned!(typeof(T.max - T.min + 1)); 74 75 enum length = cast(size_t)cast(U)(T.max - T.min + 1); 76 77 const shifted = cast(size_t)cast(U)(value - T.min); 78 79 static if (length <= 255) 80 { 81 static immutable ubyte[length] table = (){ 82 ubyte[length] ret; 83 foreach (i, member; enumMembers!T) 84 { 85 ret[member - T.min] = cast(ubyte)(i + 1); 86 } 87 return ret; 88 }(); 89 90 if (_expect(shifted < length, true)) 91 { 92 int id = table[shifted] - 1; 93 if (_expect(id >= 0, true)) 94 { 95 index = id; 96 return true; 97 } 98 } 99 return false; 100 } 101 else 102 { 103 switch (value) 104 { 105 foreach (i, member; EnumMembers!T) 106 { 107 case member: 108 index = i; 109 return true; 110 } 111 default: return false; 112 } 113 } 114 } 115 } 116 117 /// 118 @safe pure nothrow @nogc 119 version(mir_core_test) unittest 120 { 121 import std.meta: AliasSeq; 122 123 enum Common { a, b, c } 124 enum Reversed { a = 1, b = 0, c = -1 } 125 enum Shifted { a = -4, b, c } 126 enum Small { a = -4, b, c = 10 } 127 enum Big { a = -4, b, c = 1000 } 128 enum InverseBool { True = true, False = false } 129 enum FP : float { a = -4, b, c } 130 enum S : string { a = "а", b = "б", c = "ц" } 131 132 uint index = -1; 133 foreach (E; AliasSeq!(Common, Reversed, Shifted, Small, Big, FP, S)) 134 { 135 assert(getEnumIndex(E.a, index) && index == 0); 136 assert(getEnumIndex(E.b, index) && index == 1); 137 assert(getEnumIndex(E.c, index) && index == 2); 138 } 139 140 assert(getEnumIndex(InverseBool.True, index) && index == 0); 141 assert(getEnumIndex(InverseBool.False, index) && index == 1); 142 } 143 144 /++ 145 Static immutable instance of `[std.traits.EnumMembers!T]`. 146 +/ 147 template enumMembers(T) 148 if (is(T == enum)) 149 { 150 import std.traits: EnumMembers; 151 /// 152 static immutable T[EnumMembers!T.length] enumMembers = [EnumMembers!T]; 153 } 154 155 /// 156 version(mir_core_test) unittest 157 { 158 enum E {a = 1, b = -1, c} 159 static assert(enumMembers!E == [E.a, E.b, E.c]); 160 } 161 162 /++ 163 Static immutable instance of Enum Identifiers. 164 +/ 165 template enumIdentifiers(T) 166 if (is(T == enum)) 167 { 168 import std.traits: EnumMembers; 169 static immutable string[EnumMembers!T.length] enumIdentifiers = () { 170 string[EnumMembers!T.length] identifiers; 171 static foreach(i, member; EnumMembers!T) 172 identifiers[i] = __traits(identifier, EnumMembers!T[i]); 173 return identifiers; 174 } (); 175 } 176 177 /// 178 version(mir_core_test) unittest 179 { 180 enum E {z = 1, b = -1, c} 181 static assert(enumIdentifiers!E == ["z", "b", "c"]); 182 } 183 184 /++ 185 Aliases itself to $(LREF enumMembers) for string enums and 186 $(LREF enumIdentifiers) for integral and floating point enums. 187 +/ 188 template enumStrings(T) 189 if (is(T == enum)) 190 { 191 static if (is(T : C[], C)) 192 alias enumStrings = enumMembers!T; 193 else 194 alias enumStrings = enumIdentifiers!T; 195 } 196 197 /// 198 version(mir_core_test) unittest 199 { 200 enum E {z = 1, b = -1, c} 201 static assert(enumStrings!E == ["z", "b", "c"]); 202 203 enum S {a = "A", b = "B", c = ""} 204 static assert(enumStrings!S == [S.a, S.b, S.c]); 205 } 206 207 /++ 208 Params: 209 index = enum index `std.traits.EnumMembers!T` 210 Returns: 211 A enum value that corresponds to the index. 212 Note: 213 The function doesn't check that index is less then `EnumMembers!T.length`. 214 +/ 215 T unsafeEnumFromIndex(T)(size_t index) 216 @trusted pure nothrow @nogc 217 if (is(T == enum)) 218 { 219 static if (__traits(isIntegral, T)) 220 enum bool fastConv = hasSeqGrow(enumMembers!T); 221 else 222 enum bool fastConv = false; 223 224 assert(index < enumMembers!T.length); 225 226 static if (fastConv) 227 { 228 return cast(T) index; 229 } 230 else 231 { 232 return enumMembers!T[index]; 233 } 234 } 235 236 /++ 237 +/ 238 template getEnumIndexFromKey(T, bool caseInsesetive = true, getKeysTemplate...) 239 if (is(T == enum) && getKeysTemplate.length <= 1) 240 { 241 /// 242 bool getEnumIndexFromKey(C)(scope const(C)[] key, ref uint index) 243 @safe pure nothrow @nogc 244 if (is(C == char) || is(C == wchar) || is(C == dchar)) 245 { 246 import mir.string_table; 247 import mir.utility: simpleSort, _expect; 248 import std.traits: EnumMembers; 249 import std.meta: staticIndexOf; 250 251 alias String = immutable(C)[]; 252 253 static if (getKeysTemplate.length) 254 { 255 alias keysOfImpl = getKeysTemplate[0]; 256 enum String[] keysOf(alias symbol) = keysOfImpl!symbol; 257 } 258 else 259 static if (is(T : W[], W)) 260 enum String[1] keysOf(alias symbol) = [cast(String)symbol]; 261 else 262 enum String[1] keysOf(alias symbol) = [__traits(identifier, symbol)]; 263 264 enum keys = () { 265 String[] keys; 266 foreach(i, member; EnumMembers!T) 267 keys ~= keysOf!(EnumMembers!T[i]); 268 return keys; 269 } (); 270 271 static if (keys.length == 0) 272 { 273 return false; 274 } 275 else 276 { 277 enum indexLength = keys.length + 1; 278 alias ct = createTable!C; 279 static immutable table = ct!(keys, caseInsesetive); 280 static immutable indices = () 281 { 282 minimalSignedIndexType!indexLength[indexLength] indices; 283 284 foreach (i, member; EnumMembers!T) 285 foreach (key; keysOf!(EnumMembers!T[i])) 286 { 287 static if (caseInsesetive) 288 { 289 key = key.dup.fastToUpperInPlace; 290 } 291 indices[table[key]] = i; 292 } 293 294 return indices; 295 } (); 296 297 static if (!caseInsesetive && keys.length <= 6 && keys[$ - 1].length <= 32) 298 { 299 300 } 301 else 302 { 303 304 } 305 306 uint stringId = void; 307 if (_expect(table.get(key, stringId), true)) 308 { 309 index = indices[stringId]; 310 return true; 311 } 312 return false; 313 } 314 } 315 }