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                     return T.stringof ~ `@` ~ as!T.to!string; // TODO: ~ `:` ~ (*as!T).to!string;
69                 else
70                     return T.stringof ~ `@` ~ as!T.to!string;
71             }
72         }
73     }
74 
75     nothrow:
76 
77     extern (D) auto toHash() const
78     {
79         import core.internal.hash : hashOf;
80         return _raw.hashOf;
81     }
82 
83     @nogc:
84 
85     /// Construction from `value`.
86     this(T)(T value) if (canStore!T) { initialize(value); }
87     /// ditto
88     this(typeof(null) value) { _raw = S.init; }
89     /// Construction from sub-variant `value`.
90     this(SubTypes...)(WordVariant!(SubTypes) value) { initializeFromSub(value); }
91 
92     /// Assignment from `that`.
93     auto ref opAssign(typeof(this) value) { _raw = value._raw; return this; }
94     /// ditto
95     auto ref opAssign(T)(T that) if (canStore!T) { initialize(that); return this; }
96     /// ditto
97     auto ref opAssign(typeof(null) that) { _raw = S.init; return this; }
98     /// Assignment from sub-variant `value`.
99     auto ref opAssign(SubTypes...)(WordVariant!(SubTypes) value) { initializeFromSub(value); return this; }
100 
101 pragma(inline):
102 
103     /** Reference to peek of type `T`. */
104     static private struct Ref(T)
105     {
106         S _raw;
107         const bool defined;
108         pragma(inline):
109         bool opCast(T : bool)() const { return defined; }
110         inout(T) opUnary(string op : `*`)() @trusted inout
111         {
112             auto data = (_raw & ~typeMask); // mask away type bits
113             return *(cast(typeof(return)*)&data);
114         }
115     }
116 
117     @property inout(Ref!T) peek(T)() inout @trusted if (canStore!T)
118     {
119         if (!ofType!T)
120             return typeof(return).init;
121         return typeof(return)(_raw, true);
122     }
123 
124     bool opEquals(WordVariant that) const
125     {
126         return _raw == that._raw;
127     }
128 
129     bool opEquals(T)(T that) const
130     {
131         auto x = peek!T; // if `this` contains pointer of to instance of type `T`
132         return x && *x == that; // and is equal to it
133     }
134 
135     bool isNull() const
136     {
137         return _raw == S.init;
138     }
139 
140     bool opCast(T : bool)() const
141     {
142         return !isNull;
143     }
144 
145     private void initialize(T)(T that) @trusted
146     {
147         const thatRaw = (*(cast(S*)(&that)));
148         assert(!(thatRaw & typeMask), `Top-most bits of parameter is already occupied`); // TODO: use enforce instead?
149         _raw = (thatRaw | // data in lower part
150                 (cast(S)(indexOf!T + 1) << typeShift)); // use higher bits for type information
151     }
152 
153     private void initializeFromSub(SubTypes...)(WordVariant!(SubTypes) that) @trusted
154     {
155         import std.meta : staticIndexOf;
156         final switch (that.typeIndex)
157         {
158         case 0:
159             this._raw = 0;      // propagate `undefined`/`null`
160             foreach (const i, SubType; SubTypes)
161             {
162                 static assert(staticIndexOf!(SubType, Types) != -1,
163                               "Subtype " ~ SubType.stringof ~ " must be part of the supertypes " ~ Types.stringof);
164             case i + 1:
165                 this._raw = (that.rawValue | // raw value
166                              (cast(S)(indexOf!SubType + 1) << typeShift)); // super type index
167                 break;
168             }
169         }
170     }
171 
172     private bool ofType(T)() const if (canStore!T)
173     {
174         return (!isNull &&
175                 typeIndex == indexOf!T + 1);
176     }
177 
178     inout(T) as(T)() inout @trusted if (canStore!T)
179     {
180         // only in debug mode because it's meant to be called in conjunction with `typeIndex`
181         assert(ofType!T);
182         inout x = rawValue;
183         return *(cast(typeof(return)*)(cast(void*)&x)); // reinterpret
184     }
185 
186     /** Get zero-offset index as `Ix` of current variant type. */
187     Ix typeIx() const
188     {
189         return cast(typeof(return))typeIndex;
190     }
191 
192     /** Get zero-offset index of current variant type. */
193     private auto typeIndex() const
194     {
195         return ((_raw & typeMask) >> typeShift);
196     }
197 
198     private S rawValue() const
199     {
200         return _raw & ~typeMask;
201     }
202 
203     private S _raw;             // raw untyped word
204 
205 }
206 
207 /* TODO: activate to figure out what makes Sample instance consume RAM */
208 version(debugMemoryUsage)
209 {
210 	struct S {}
211 	struct T {}
212 	alias Sample = WordVariant!(S, T);
213 }
214 
215 ///
216 pure nothrow unittest
217 {
218     import std.meta : AliasSeq;
219 
220     alias SubType = WordVariant!(byte*, short*);
221     alias SuperType = WordVariant!(bool*, byte*, short*, long*);
222 
223     byte* byteValue = cast(byte*)(0x11);
224     short* shortValue = cast(short*)(0x22);
225 
226     SubType sub = byteValue;
227     assert(sub.typeIndex == 1);
228     assert(sub.peek!(byte*));
229     assert(*(sub.peek!(byte*)) == byteValue);
230 
231     SuperType sup = sub;
232     assert(sup.typeIndex == 2);
233     assert(sup.peek!(byte*));
234     assert(*(sup.peek!(byte*)) == byteValue);
235 
236     sub = shortValue;
237     assert(sub.typeIndex == 2);
238     assert(sub.peek!(short*));
239     assert(*(sub.peek!(short*)) == shortValue);
240 
241     sup = sub;
242     assert(sup.typeIndex == 3);
243     assert(sup.peek!(short*));
244     assert(*(sup.peek!(short*)) == shortValue);
245 }
246 
247 ///
248 pure nothrow unittest
249 {
250     import std.meta : AliasSeq;
251 
252     alias Types = AliasSeq!(byte*, short*, int*, long*,
253                             ubyte*, ushort*, uint*, ulong*,
254                             float*, double*, real*,
255                             char*, wchar*, dchar*);
256 
257     alias V = WordVariant!Types;
258     V v;
259 
260     try
261     {
262         assert(v.toString == "null");
263     }
264     catch (Exception e) {}
265 
266     assert(v.isNull);
267     v = null;
268     assert(v.isNull);
269     assert(!v);
270 
271     foreach (Tp; Types)
272     {
273         alias T = typeof(*Tp.init);
274 
275         static assert(!__traits(compiles, { T[] a; v = &a; }));
276         static assert(!__traits(compiles, { v.peek!(T[]*); }));
277 
278         // assignment from stack pointer
279         T a = 73;
280         T a_ = 73;
281 
282         v = &a;
283         assert(v);
284         assert(!v.isNull);
285         assert(v.typeIndex != 0);
286         assert(v.ofType!Tp);
287 
288         assert(v == &a);
289         assert(v != &a_);
290         assert(v);
291 
292         foreach (Up; Types)
293         {
294             alias U = typeof(*Up.init);
295             static if (is(T == U))
296             {
297                 assert(v.peek!Up);
298                 assert(*(v.peek!Up) == &a);
299                 assert(v.as!Up == &a);
300             }
301             else
302                 assert(!v.peek!Up);
303         }
304 
305         // assignment from heap pointer
306         T* b = new T;
307         T* b_ = new T;
308         *b = 73;
309         *b_ = 73;
310         v = b;
311         assert(v == b);
312         assert(v != b_);
313         assert(v);
314         foreach (Up; Types)
315         {
316             alias U = typeof(*Up.init);
317             static if (is(T == U))
318             {
319                 assert(v.peek!Up);
320                 assert(*(v.peek!Up) == b);
321             }
322             else
323                 assert(!v.peek!Up);
324         }
325 
326     }
327 }
328 
329 /** A typed pointer to a variant of `Types`, packed into a word (`size_t`).
330  *
331  * See_Also: `std.bitmanip.taggedPointer`.
332  */
333 struct VariantPointerTo(Types...)
334 {
335     static assert(this.sizeof == (void*).sizeof, "Size must be same as word (pointer)");
336 
337     alias S = size_t; // TODO: templatize?
338 
339     import nxt.bit_traits : bitsNeeded;
340     enum typeBits = bitsNeeded!(Types.length); // number of bits needed to represent variant type
341     enum typeShift = 8*S.sizeof - typeBits;
342     enum typeMask = cast(S)(2^^typeBits - 1) << typeShift;
343 
344     import std.meta : staticIndexOf;
345     enum indexOf(T) = staticIndexOf!(T, Types); // TODO: cast to ubyte if Types.length is <= 256
346 
347     /// Is `true` iff a pointer to a `T` can be stored.
348     enum canStorePointerTo(T) = indexOf!T >= 0;
349 
350     pure:
351 
352     @property string toString() const @trusted // TODO: pure
353     {
354         import std.conv : to;
355         final switch (typeIndex)
356         {
357             foreach (const i, T; Types)
358             {
359             case i: return T.stringof ~ `@` ~ ptr.to!string;
360             }
361         }
362     }
363 
364     nothrow:
365 
366     extern (D) auto toHash() const
367     {
368         import core.internal.hash : hashOf;
369         return _raw.hashOf;
370     }
371 
372     @nogc:
373 
374     /// Construction from `value`.
375     this(T)(T* value)
376     if (canStorePointerTo!T)
377     {
378         init(value);
379     }
380     /// ditto
381     this(typeof(null) value) { /* null is the default */ }
382 
383     /// Assignment from `that`.
384     auto ref opAssign(T)(T* that)
385     if (canStorePointerTo!T)
386     {
387         init(that);
388         return this;
389     }
390     /// ditto
391     auto ref opAssign(typeof(null) that)
392     {
393         _raw = S.init;
394         return this;
395     }
396 
397     @property inout(void)* ptr() inout @trusted
398     {
399         return cast(void*)(_raw & ~typeMask);
400     }
401 
402     @property inout(T)* peek(T)() inout @trusted if (canStorePointerTo!T)
403     {
404         static if (is(T == void))
405             static assert(canStorePointerTo!T, `Cannot store a ` ~ T.stringof ~ ` in a ` ~ name);
406         if (!pointsTo!T)
407             return null;
408         return cast(inout T*)ptr;
409     }
410 
411     bool opEquals(T)(T* that) const @trusted
412     {
413         auto x = peek!T; // if `this` contains pointer of to instance of type `T`
414         return (x &&
415                 x == that); // and is equal to it
416     }
417 
418     bool isNull() const
419     {
420         return ptr is null;
421     }
422 
423     bool opCast(T : bool)() const
424     {
425         return ptr !is null;
426     }
427 
428     private void init(T)(T* that)
429     {
430         const thatRaw = cast(S)that;
431         assert(!(thatRaw & typeMask), `Top-most bits of pointer are already occupied`); // TODO: use enforce instead?
432         _raw = (thatRaw | // pointer in lower part
433                 (cast(S)(indexOf!T) << typeShift)); // use higher bits for type information
434     }
435 
436     /** Get zero-offset index of current variant type. */
437     private auto typeIndex() const
438     {
439         return (_raw & typeMask) >> typeShift;
440     }
441 
442     private bool pointsTo(T)() const if (canStorePointerTo!T)
443     {
444         return typeIndex == indexOf!T;
445     }
446 
447     private S _raw;             // raw untyped word
448 
449 }
450 
451 ///
452 pure nothrow unittest
453 {
454     import std.meta : AliasSeq;
455 
456     alias Types = AliasSeq!(byte, short, int, long,
457                             ubyte, ushort, uint, ulong,
458                             float, double, real,
459                             char, wchar, dchar);
460 
461     alias V = VariantPointerTo!Types;
462 
463     V v;
464     assert(v.isNull);
465     v = null;
466     assert(v.isNull);
467     assert(!v);
468 
469     foreach (T; Types)
470     {
471         static assert(!__traits(compiles, { T[] a; v = &a; }));
472         static assert(!__traits(compiles, { v.peek!(T[]*); }));
473 
474         // assignment from stack pointer
475         T a = 73;
476         T a_ = 73;
477         v = &a;
478         assert(v == &a);
479         assert(v != &a_);
480         assert(v);
481         foreach (U; Types)
482         {
483             static if (is(T == U))
484             {
485                 assert(v.peek!U);
486                 assert(*(v.peek!U) == a);
487             }
488             else
489                 assert(!v.peek!U);
490         }
491 
492         // assignment from heap pointer
493         T* b = new T;
494         T* b_ = new T;
495         *b = 73;
496         *b_ = 73;
497         v = b;
498         assert(v == b);
499         assert(v != b_);
500         assert(v);
501         foreach (U; Types)
502         {
503             static if (is(T == U))
504             {
505                 assert(v.peek!U);
506                 assert(*(v.peek!U) == *b);
507             }
508             else
509                 assert(!v.peek!U);
510         }
511 
512     }
513 }