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 }