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 	import std.meta : AliasSeq;
218 
219 	alias SubType = WordVariant!(byte*, short*);
220 	alias SuperType = WordVariant!(bool*, byte*, short*, long*);
221 
222 	byte* byteValue = cast(byte*)(0x11);
223 	short* shortValue = cast(short*)(0x22);
224 
225 	SubType sub = byteValue;
226 	assert(sub.typeIndex == 1);
227 	assert(sub.peek!(byte*));
228 	assert(*(sub.peek!(byte*)) == byteValue);
229 
230 	SuperType sup = sub;
231 	assert(sup.typeIndex == 2);
232 	assert(sup.peek!(byte*));
233 	assert(*(sup.peek!(byte*)) == byteValue);
234 
235 	sub = shortValue;
236 	assert(sub.typeIndex == 2);
237 	assert(sub.peek!(short*));
238 	assert(*(sub.peek!(short*)) == shortValue);
239 
240 	sup = sub;
241 	assert(sup.typeIndex == 3);
242 	assert(sup.peek!(short*));
243 	assert(*(sup.peek!(short*)) == shortValue);
244 }
245 
246 ///
247 pure nothrow unittest {
248 	import std.meta : AliasSeq;
249 
250 	alias Types = AliasSeq!(byte*, short*, int*, long*,
251 							ubyte*, ushort*, uint*, ulong*,
252 							float*, double*, real*,
253 							char*, wchar*, dchar*);
254 
255 	alias V = WordVariant!Types;
256 	V v;
257 
258 	try
259 	{
260 		assert(v.toString == "null");
261 	}
262 	catch (Exception e) {}
263 
264 	assert(v.isNull);
265 	v = null;
266 	assert(v.isNull);
267 	assert(!v);
268 
269 	foreach (Tp; Types)
270 	{
271 		alias T = typeof(*Tp.init);
272 
273 		static assert(!__traits(compiles, { T[] a; v = &a; }));
274 		static assert(!__traits(compiles, { v.peek!(T[]*); }));
275 
276 		// assignment from stack pointer
277 		T a = 73;
278 		T a_ = 73;
279 
280 		v = &a;
281 		assert(v);
282 		assert(!v.isNull);
283 		assert(v.typeIndex != 0);
284 		assert(v.ofType!Tp);
285 
286 		assert(v == &a);
287 		assert(v != &a_);
288 		assert(v);
289 
290 		foreach (Up; Types)
291 		{
292 			alias U = typeof(*Up.init);
293 			static if (is(T == U))
294 			{
295 				assert(v.peek!Up);
296 				assert(*(v.peek!Up) == &a);
297 				assert(v.as!Up == &a);
298 			}
299 			else
300 				assert(!v.peek!Up);
301 		}
302 
303 		// assignment from heap pointer
304 		T* b = new T;
305 		T* b_ = new T;
306 		*b = 73;
307 		*b_ = 73;
308 		v = b;
309 		assert(v == b);
310 		assert(v != b_);
311 		assert(v);
312 		foreach (Up; Types)
313 		{
314 			alias U = typeof(*Up.init);
315 			static if (is(T == U))
316 			{
317 				assert(v.peek!Up);
318 				assert(*(v.peek!Up) == b);
319 			}
320 			else
321 				assert(!v.peek!Up);
322 		}
323 
324 	}
325 }
326 
327 /** A typed pointer to a variant of `Types`, packed into a word (`size_t`).
328  *
329  * See_Also: `std.bitmanip.taggedPointer`.
330  */
331 struct VariantPointerTo(Types...)
332 {
333 	static assert(this.sizeof == (void*).sizeof, "Size must be same as word (pointer)");
334 
335 	alias S = size_t; /+ TODO: templatize? +/
336 
337 	import nxt.bit_traits : bitsNeeded;
338 	enum typeBits = bitsNeeded!(Types.length); // number of bits needed to represent variant type
339 	enum typeShift = 8*S.sizeof - typeBits;
340 	enum typeMask = cast(S)(2^^typeBits - 1) << typeShift;
341 
342 	import std.meta : staticIndexOf;
343 	enum indexOf(T) = staticIndexOf!(T, Types); /+ TODO: cast to ubyte if Types.length is <= 256 +/
344 
345 	/// Is `true` iff a pointer to a `T` can be stored.
346 	enum canStorePointerTo(T) = indexOf!T >= 0;
347 
348 	pure:
349 
350 	@property string toString() const @trusted /+ TODO: pure +/
351 	{
352 		import std.conv : to;
353 		final switch (typeIndex)
354 		{
355 			foreach (const i, T; Types)
356 			{
357 			case i: return T.stringof ~ `@` ~ ptr.to!string;
358 			}
359 		}
360 	}
361 
362 	nothrow:
363 
364 	extern (D) auto toHash() const
365 	{
366 		import core.internal.hash : hashOf;
367 		return _raw.hashOf;
368 	}
369 
370 	@nogc:
371 
372 	/// Construction from `value`.
373 	this(T)(T* value)
374 	if (canStorePointerTo!T)
375 	{
376 		init(value);
377 	}
378 	/// ditto
379 	this(typeof(null) value) { /* null is the default */ }
380 
381 	/// Assignment from `that`.
382 	auto ref opAssign(T)(T* that)
383 	if (canStorePointerTo!T)
384 	{
385 		init(that);
386 		return this;
387 	}
388 	/// ditto
389 	auto ref opAssign(typeof(null) that)
390 	{
391 		_raw = S.init;
392 		return this;
393 	}
394 
395 	@property inout(void)* ptr() inout @trusted
396 	{
397 		return cast(void*)(_raw & ~typeMask);
398 	}
399 
400 	@property inout(T)* peek(T)() inout @trusted if (canStorePointerTo!T)
401 	{
402 		static if (is(T == void))
403 			static assert(canStorePointerTo!T, `Cannot store a ` ~ T.stringof ~ ` in a ` ~ name);
404 		if (!pointsTo!T)
405 			return null;
406 		return cast(inout T*)ptr;
407 	}
408 
409 	bool opEquals(T)(T* that) const @trusted
410 	{
411 		auto x = peek!T; // if `this` contains pointer of to instance of type `T`
412 		return (x &&
413 				x == that); // and is equal to it
414 	}
415 
416 	bool isNull() const
417 	{
418 		return ptr is null;
419 	}
420 
421 	bool opCast(T : bool)() const
422 	{
423 		return ptr !is null;
424 	}
425 
426 	private void init(T)(T* that)
427 	{
428 		const thatRaw = cast(S)that;
429 		assert(!(thatRaw & typeMask), `Top-most bits of pointer are already occupied`); /+ TODO: use enforce instead? +/
430 		_raw = (thatRaw | // pointer in lower part
431 				(cast(S)(indexOf!T) << typeShift)); // use higher bits for type information
432 	}
433 
434 	/** Get zero-offset index of current variant type. */
435 	private auto typeIndex() const
436 	{
437 		return (_raw & typeMask) >> typeShift;
438 	}
439 
440 	private bool pointsTo(T)() const if (canStorePointerTo!T)
441 	{
442 		return typeIndex == indexOf!T;
443 	}
444 
445 	private S _raw;			 // raw untyped word
446 
447 }
448 
449 ///
450 pure nothrow unittest {
451 	import std.meta : AliasSeq;
452 
453 	alias Types = AliasSeq!(byte, short, int, long,
454 							ubyte, ushort, uint, ulong,
455 							float, double, real,
456 							char, wchar, dchar);
457 
458 	alias V = VariantPointerTo!Types;
459 
460 	V v;
461 	assert(v.isNull);
462 	v = null;
463 	assert(v.isNull);
464 	assert(!v);
465 
466 	foreach (T; Types)
467 	{
468 		static assert(!__traits(compiles, { T[] a; v = &a; }));
469 		static assert(!__traits(compiles, { v.peek!(T[]*); }));
470 
471 		// assignment from stack pointer
472 		T a = 73;
473 		T a_ = 73;
474 		v = &a;
475 		assert(v == &a);
476 		assert(v != &a_);
477 		assert(v);
478 		foreach (U; Types)
479 		{
480 			static if (is(T == U))
481 			{
482 				assert(v.peek!U);
483 				assert(*(v.peek!U) == a);
484 			}
485 			else
486 				assert(!v.peek!U);
487 		}
488 
489 		// assignment from heap pointer
490 		T* b = new T;
491 		T* b_ = new T;
492 		*b = 73;
493 		*b_ = 73;
494 		v = b;
495 		assert(v == b);
496 		assert(v != b_);
497 		assert(v);
498 		foreach (U; Types)
499 		{
500 			static if (is(T == U))
501 			{
502 				assert(v.peek!U);
503 				assert(*(v.peek!U) == *b);
504 			}
505 			else
506 				assert(!v.peek!U);
507 		}
508 
509 	}
510 }