1 /** Lightweight versions of polymorphism packed inside one single 2 word/pointer. 3 4 Most significant bits are used to store type information. 5 6 These higher bits are normally unused on 64-bit systems (tested on 7 Linux). 16 higher bits are, on Linux, either 1 (kernel-space) or 0 (user-space). 8 9 See_Also: http://forum.dlang.org/post/sybuoliqhhefcovxjfjv@forum.dlang.org 10 11 TODO Ask forum.dlang.org: Is it safe to assume that `typeBits` most significant bits of a 12 pointer are zero? If not put them in least significant part. 13 14 TODO What todo with the fact that the GC will fail to scan WordVariant? 15 Can the GC be tweaked to mask out the type bits before scanning? 16 17 TODO Enable support for `is null` instead of `isNull`? 18 19 TODO Use `enforce()` instead of `assert()` in WordVariant:initialize() 20 21 TODO Move to Phobos std.variant 22 */ 23 module nxt.variant_ex; 24 25 /** A variant of `Types` packed into a word (`size_t`). 26 * 27 * Suitable for use in tree-data containers, such as radix trees (tries), where 28 * hybrid value (sparsely packed sub-tree) and pointer (to dense sub-tree) 29 * packing of sub-nodes is needed. 30 */ 31 struct WordVariant(Types...) 32 { 33 import nxt.traits_ex : allSame, sizesOf; 34 static assert(allSame!(sizesOf!(Types)), "Types must have equal size"); 35 static assert(this.sizeof == (void*).sizeof, "Size must be same as word (pointer)"); 36 37 alias S = size_t; // TODO templatize? 38 39 import nxt.typecons_ex : makeEnumFromSymbolNames; 40 alias Ix = makeEnumFromSymbolNames!(`ix_`, ``, true, false, Types); 41 static assert(Ix.undefined == 0); 42 43 import nxt.bit_traits : bitsNeeded; 44 enum typeBits = bitsNeeded!(1 + Types.length); // number of bits needed to represent variant type, plus one for undefined state 45 enum typeShift = 8*S.sizeof - typeBits; 46 enum typeMask = cast(S)(2^^typeBits - 1) << typeShift; 47 48 import std.meta : staticIndexOf; 49 enum indexOf(T) = staticIndexOf!(T, Types); // TODO cast to ubyte if Types.length is <= 256 50 51 /// Is `true` iff a `T` can be stored. 52 enum canStore(T) = indexOf!T >= 0; 53 54 pure: 55 56 @property string toString() const @trusted // TODO pure nothrow 57 { 58 import std.traits : isPointer; 59 import std.conv : to; 60 final switch (typeIndex) // typeIndex starts at 0 (undefined) 61 { 62 case 0: return "null"; 63 foreach (const i, T; Types) 64 { 65 case i + 1: 66 // TODO improve this to look more like D-code: 67 static if (isPointer!T) 68 { 69 return T.stringof ~ `@` ~ as!T.to!string; // TODO ~ `:` ~ (*as!T).to!string; 70 } 71 else 72 { 73 return T.stringof ~ `@` ~ as!T.to!string; 74 } 75 } 76 } 77 } 78 79 nothrow: 80 81 extern (D) auto toHash() const 82 { 83 import core.internal.hash : hashOf; 84 return _raw.hashOf; 85 } 86 87 @nogc: 88 89 /// Construction from `value`. 90 this(T)(T value) if (canStore!T) { initialize(value); } 91 /// ditto 92 this(typeof(null) value) { _raw = S.init; } 93 /// Construction from sub-variant `value`. 94 this(SubTypes...)(WordVariant!(SubTypes) value) { initializeFromSub(value); } 95 96 /// Assignment from `that`. 97 auto ref opAssign(typeof(this) value) { _raw = value._raw; return this; } 98 /// ditto 99 auto ref opAssign(T)(T that) if (canStore!T) { initialize(that); return this; } 100 /// ditto 101 auto ref opAssign(typeof(null) that) { _raw = S.init; return this; } 102 /// Assignment from sub-variant `value`. 103 auto ref opAssign(SubTypes...)(WordVariant!(SubTypes) value) { initializeFromSub(value); return this; } 104 105 pragma(inline): 106 107 /** Reference to peek of type `T`. */ 108 static private struct Ref(T) 109 { 110 S _raw; 111 const bool defined; 112 pragma(inline): 113 bool opCast(T : bool)() const { return defined; } 114 inout(T) opUnary(string op : `*`)() @trusted inout 115 { 116 auto data = (_raw & ~typeMask); // mask away type bits 117 return *(cast(typeof(return)*)&data); 118 } 119 } 120 121 @property inout(Ref!T) peek(T)() inout @trusted if (canStore!T) 122 { 123 if (!ofType!T) { return typeof(return).init; } 124 return typeof(return)(_raw, true); 125 } 126 127 bool opEquals(WordVariant that) const 128 { 129 return _raw == that._raw; 130 } 131 132 bool opEquals(T)(T that) const 133 { 134 auto x = peek!T; // if `this` contains pointer of to instance of type `T` 135 return x && *x == that; // and is equal to it 136 } 137 138 bool isNull() const { return _raw == S.init; } 139 140 bool opCast(T : bool)() const { return !isNull; } 141 142 private void initialize(T)(T that) @trusted 143 { 144 const thatRaw = (*(cast(S*)(&that))); 145 assert(!(thatRaw & typeMask), `Top-most bits of parameter is already occupied`); // TODO use enforce instead? 146 _raw = (thatRaw | // data in lower part 147 (cast(S)(indexOf!T + 1) << typeShift)); // use higher bits for type information 148 } 149 150 private void initializeFromSub(SubTypes...)(WordVariant!(SubTypes) that) @trusted 151 { 152 import std.meta : staticIndexOf; 153 final switch (that.typeIndex) 154 { 155 case 0: 156 this._raw = 0; // propagate `undefined`/`null` 157 foreach (const i, SubType; SubTypes) 158 { 159 static assert(staticIndexOf!(SubType, Types) != -1, 160 "Subtype " ~ SubType.stringof ~ " must be part of the supertypes " ~ Types.stringof); 161 case i + 1: 162 this._raw = (that.rawValue | // raw value 163 (cast(S)(indexOf!SubType + 1) << typeShift)); // super type index 164 break; 165 } 166 } 167 } 168 169 private bool ofType(T)() const if (canStore!T) 170 { 171 return !isNull && typeIndex == indexOf!T + 1; 172 } 173 174 inout(T) as(T)() inout @trusted if (canStore!T) 175 { 176 // only in debug mode because it's meant to be called in conjunction with `typeIndex` 177 assert(ofType!T); 178 inout x = rawValue; 179 return *(cast(typeof(return)*)(cast(void*)&x)); // reinterpret 180 } 181 182 /** Get zero-offset index as `Ix` of current variant type. */ 183 Ix typeIx() const 184 { 185 return cast(typeof(return))typeIndex; 186 } 187 188 /** Get zero-offset index of current variant type. */ 189 private auto typeIndex() const 190 { 191 return ((_raw & typeMask) >> typeShift); 192 } 193 194 private S rawValue() const { return _raw & ~typeMask; } 195 196 private S _raw; // raw untyped word 197 198 } 199 200 /// 201 pure nothrow unittest 202 { 203 import std.meta : AliasSeq; 204 205 alias SubType = WordVariant!(byte*, short*); 206 alias SuperType = WordVariant!(bool*, byte*, short*, long*); 207 208 byte* byteValue = cast(byte*)(0x11); 209 short* shortValue = cast(short*)(0x22); 210 211 SubType sub = byteValue; 212 assert(sub.typeIndex == 1); 213 assert(sub.peek!(byte*)); 214 assert(*(sub.peek!(byte*)) == byteValue); 215 216 SuperType sup = sub; 217 assert(sup.typeIndex == 2); 218 assert(sup.peek!(byte*)); 219 assert(*(sup.peek!(byte*)) == byteValue); 220 221 sub = shortValue; 222 assert(sub.typeIndex == 2); 223 assert(sub.peek!(short*)); 224 assert(*(sub.peek!(short*)) == shortValue); 225 226 sup = sub; 227 assert(sup.typeIndex == 3); 228 assert(sup.peek!(short*)); 229 assert(*(sup.peek!(short*)) == shortValue); 230 } 231 232 /// 233 pure nothrow unittest 234 { 235 import std.meta : AliasSeq; 236 237 alias Types = AliasSeq!(byte*, short*, int*, long*, 238 ubyte*, ushort*, uint*, ulong*, 239 float*, double*, real*, 240 char*, wchar*, dchar*); 241 242 alias V = WordVariant!Types; 243 V v; 244 245 try { assert(v.toString == "null"); } catch (Exception e) { } 246 247 assert(v.isNull); 248 v = null; 249 assert(v.isNull); 250 assert(!v); 251 252 foreach (Tp; Types) 253 { 254 alias T = typeof(*Tp.init); 255 256 static assert(!__traits(compiles, { T[] a; v = &a; })); 257 static assert(!__traits(compiles, { v.peek!(T[]*); })); 258 259 // assignment from stack pointer 260 T a = 73; 261 T a_ = 73; 262 263 v = &a; 264 assert(v); 265 assert(!v.isNull); 266 assert(v.typeIndex != 0); 267 assert(v.ofType!Tp); 268 269 assert(v == &a); 270 assert(v != &a_); 271 assert(v); 272 273 foreach (Up; Types) 274 { 275 alias U = typeof(*Up.init); 276 static if (is(T == U)) 277 { 278 assert(v.peek!Up); 279 assert(*(v.peek!Up) == &a); 280 assert(v.as!Up == &a); 281 } 282 else 283 { 284 assert(!v.peek!Up); 285 } 286 } 287 288 // assignment from heap pointer 289 T* b = new T; 290 T* b_ = new T; 291 *b = 73; 292 *b_ = 73; 293 v = b; 294 assert(v == b); 295 assert(v != b_); 296 assert(v); 297 foreach (Up; Types) 298 { 299 alias U = typeof(*Up.init); 300 static if (is(T == U)) 301 { 302 assert(v.peek!Up); 303 assert(*(v.peek!Up) == b); 304 } 305 else 306 { 307 assert(!v.peek!Up); 308 } 309 } 310 311 } 312 } 313 314 /** A typed pointer to a variant of `Types`, packed into a word (`size_t`). 315 * 316 * See_Also: `std.bitmanip.taggedPointer`. 317 */ 318 struct VariantPointerTo(Types...) 319 { 320 static assert(this.sizeof == (void*).sizeof, "Size must be same as word (pointer)"); 321 322 alias S = size_t; // TODO templatize? 323 324 import nxt.bit_traits : bitsNeeded; 325 enum typeBits = bitsNeeded!(Types.length); // number of bits needed to represent variant type 326 enum typeShift = 8*S.sizeof - typeBits; 327 enum typeMask = cast(S)(2^^typeBits - 1) << typeShift; 328 329 import std.meta : staticIndexOf; 330 enum indexOf(T) = staticIndexOf!(T, Types); // TODO cast to ubyte if Types.length is <= 256 331 332 /// Is `true` iff a pointer to a `T` can be stored. 333 enum canStorePointerTo(T) = indexOf!T >= 0; 334 335 pure: 336 337 @property string toString() const @trusted // TODO pure 338 { 339 import std.conv : to; 340 final switch (typeIndex) 341 { 342 foreach (const i, T; Types) 343 { 344 case i: return T.stringof ~ `@` ~ ptr.to!string; 345 } 346 } 347 } 348 349 nothrow: 350 351 extern (D) auto toHash() const 352 { 353 import core.internal.hash : hashOf; 354 return _raw.hashOf; 355 } 356 357 @nogc: 358 359 /// Construction from `value`. 360 this(T)(T* value) if (canStorePointerTo!T) { init(value); } 361 /// ditto 362 this(typeof(null) value) { /* null is the default */ } 363 364 /// Assignment from `that`. 365 auto ref opAssign(T)(T* that) if (canStorePointerTo!T) { init(that); return this; } 366 /// ditto 367 auto ref opAssign(typeof(null) that) { _raw = S.init; return this; } 368 369 @property inout(void)* ptr() inout @trusted { return cast(void*)(_raw & ~typeMask); } 370 371 @property inout(T)* peek(T)() inout @trusted if (canStorePointerTo!T) 372 { 373 static if (is(T == void)) static assert(canStorePointerTo!T, `Cannot store a ` ~ T.stringof ~ ` in a ` ~ name); 374 if (!pointsTo!T) { return null; } 375 return cast(inout T*)ptr; 376 } 377 378 bool opEquals(T)(T* that) const @trusted 379 { 380 auto x = peek!T; // if `this` contains pointer of to instance of type `T` 381 return x && x == that; // and is equal to it 382 } 383 384 bool isNull() const { return ptr is null; } 385 386 bool opCast(T : bool)() const { return ptr !is null; } 387 388 private void init(T)(T* that) 389 { 390 const thatRaw = cast(S)that; 391 assert(!(thatRaw & typeMask), `Top-most bits of pointer are already occupied`); // TODO use enforce instead? 392 _raw = (thatRaw | // pointer in lower part 393 (cast(S)(indexOf!T) << typeShift)); // use higher bits for type information 394 } 395 396 /** Get zero-offset index of current variant type. */ 397 private auto typeIndex() const 398 { 399 return (_raw & typeMask) >> typeShift; 400 } 401 402 private bool pointsTo(T)() const if (canStorePointerTo!T) 403 { 404 return typeIndex == indexOf!T; 405 } 406 407 private S _raw; // raw untyped word 408 409 } 410 411 /// 412 pure nothrow unittest 413 { 414 import std.meta : AliasSeq; 415 416 alias Types = AliasSeq!(byte, short, int, long, 417 ubyte, ushort, uint, ulong, 418 float, double, real, 419 char, wchar, dchar); 420 421 alias V = VariantPointerTo!Types; 422 423 V v; 424 assert(v.isNull); 425 v = null; 426 assert(v.isNull); 427 assert(!v); 428 429 foreach (T; Types) 430 { 431 static assert(!__traits(compiles, { T[] a; v = &a; })); 432 static assert(!__traits(compiles, { v.peek!(T[]*); })); 433 434 // assignment from stack pointer 435 T a = 73; 436 T a_ = 73; 437 v = &a; 438 assert(v == &a); 439 assert(v != &a_); 440 assert(v); 441 foreach (U; Types) 442 { 443 static if (is(T == U)) 444 { 445 assert(v.peek!U); 446 assert(*(v.peek!U) == a); 447 } 448 else 449 { 450 assert(!v.peek!U); 451 } 452 } 453 454 // assignment from heap pointer 455 T* b = new T; 456 T* b_ = new T; 457 *b = 73; 458 *b_ = 73; 459 v = b; 460 assert(v == b); 461 assert(v != b_); 462 assert(v); 463 foreach (U; Types) 464 { 465 static if (is(T == U)) 466 { 467 assert(v.peek!U); 468 assert(*(v.peek!U) == *b); 469 } 470 else 471 { 472 assert(!v.peek!U); 473 } 474 } 475 476 } 477 }