1 module rt.util.typeinfo;
2 
3 import rt.util.utility;
4 
5 version(LWDR_DynamicArray)
6 {
7     version = LWDR_INTERNAL_ti_next;
8 }
9 
10 // Reduces to `T` if `cond` is `true` or `U` otherwise.
11 private template Select(bool cond, T, U)
12 {
13     static if (cond) alias Select = T;
14     else alias Select = U;
15 }
16 
17 template Floating(T)
18 if (is(T == float) || is(T == double) || is(T == real))
19 {
20 	pure nothrow @safe:
21 
22     bool equals(T f1, T f2)
23     {
24         return f1 == f2;
25     }
26 
27     int compare(T d1, T d2)
28     {
29         if (d1 != d1 || d2 != d2) // if either are NaN
30         {
31             if (d1 != d1)
32             {
33                 if (d2 != d2)
34                     return 0;
35                 return -1;
36             }
37             return 1;
38         }
39         return (d1 == d2) ? 0 : ((d1 < d2) ? -1 : 1);
40     }
41 }
42 
43 // @@@DEPRECATED_2.105@@@
44 template Floating(T)
45 if (isComplex!T)
46 {
47 	pure nothrow @safe:
48 
49     bool equals(T f1, T f2)
50     {
51         return f1.re == f2.re && f1.im == f2.im;
52     }
53 
54     int compare(T f1, T f2)
55     {
56         int result;
57 
58         if (f1.re < f2.re)
59             result = -1;
60         else if (f1.re > f2.re)
61             result = 1;
62         else if (f1.im < f2.im)
63             result = -1;
64         else if (f1.im > f2.im)
65             result = 1;
66         else
67             result = 0;
68         return result;
69     }
70 
71     size_t hashOf(scope const T val)
72     {
73         return 0;
74         //return core.internal.hash.hashOf(val.re, core.internal.hash.hashOf(val.im));
75     }
76 }
77 
78 template Array(T)
79 if (is(T == float) || is(T == double) || is(T == real))
80 {
81 	pure nothrow @safe:
82 
83     bool equals(T[] s1, T[] s2)
84     {
85         size_t len = s1.length;
86         if (len != s2.length)
87             return false;
88         for (size_t u = 0; u < len; u++)
89         {
90             if (!Floating!T.equals(s1[u], s2[u]))
91                 return false;
92         }
93         return true;
94     }
95 
96     int compare(T[] s1, T[] s2)
97     {
98         size_t len = s1.length;
99         if (s2.length < len)
100             len = s2.length;
101         for (size_t u = 0; u < len; u++)
102         {
103             if (int c = Floating!T.compare(s1[u], s2[u]))
104                 return c;
105         }
106         return (s1.length > s2.length) - (s1.length < s2.length);
107     }
108 
109     //public alias hashOf = core.internal.hash.hashOf;
110 }
111 
112 // @@@DEPRECATED_2.105@@@
113 template Array(T)
114 if (isComplex!T)
115 {
116 	pure nothrow @safe:
117 
118     bool equals(T[] s1, T[] s2)
119     {
120         size_t len = s1.length;
121         if (len != s2.length)
122             return false;
123         for (size_t u = 0; u < len; u++)
124         {
125             if (!Floating!T.equals(s1[u], s2[u]))
126                 return false;
127         }
128         return true;
129     }
130 
131     int compare(T[] s1, T[] s2)
132     {
133         size_t len = s1.length;
134         if (s2.length < len)
135             len = s2.length;
136         for (size_t u = 0; u < len; u++)
137         {
138             if (int c = Floating!T.compare(s1[u], s2[u]))
139                 return c;
140         }
141         return (s1.length > s2.length) - (s1.length < s2.length);
142     }
143 
144     size_t hashOf(scope const T[] val)
145     {
146         return 0;
147         /+size_t hash = 0;
148         foreach (ref o; val)
149         {
150 		hash = core.internal.hash.hashOf(Floating!T.hashOf(o), hash);
151         }
152         return hash;+/
153     }
154 }
155 
156 
157 
158 /*
159 TypeInfo information for built-in types.
160 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
161 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
162 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
163 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
164 during compilation whether they have different signedness and override appropriately. For initializer, we
165 detect if we need to override. The overriding initializer should be nonzero.
166 */
167 private class TypeInfoGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo, TypeInfoGeneric!Base)
168 if (T.sizeof == Base.sizeof && T.alignof == Base.alignof)
169 {
170 const: nothrow: pure: @trusted:
171 
172     // Returns the type name.
173     override string toString() const pure nothrow @safe { return T.stringof; }
174 
175     // `getHash` is the same for `Base` and `T`, introduce it just once.
176     static if (is(T == Base))
177         /+override size_t getHash(scope const void* p)
178         {
179 		static if (__traits(isFloating, T) || isComplex!T)
180 		return Floating!T.hashOf(*cast(T*)p);
181 		else
182 		return hashOf(*cast(const T *)p);
183         }+/
184 
185 		// `equals` is the same for `Base` and `T`, introduce it just once.
186 		static if (is(T == Base))
187 			override bool equals(in void* p1, in void* p2)
188 			{
189 				static if (__traits(isFloating, T) || isComplex!T)
190 					return Floating!T.equals(*cast(T*)p1, *cast(T*)p2);
191 				else
192 					return *cast(T *)p1 == *cast(T *)p2;
193 			}
194 
195     // `T` and `Base` may have different signedness, so this function is introduced conditionally.
196     static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max))
197         /+override int compare(in void* p1, in void* p2)
198         {
199 		static if (__traits(isFloating, T) || isComplex!T)
200 		{
201 		return Floating!T.compare(*cast(T*)p1, *cast(T*)p2);
202 		}
203 		else static if (T.sizeof < int.sizeof)
204 		{
205 		// Taking the difference will always fit in an int.
206 		return int(*cast(T *) p1) - int(*cast(T *) p2);
207 		}
208 		else
209 		{
210 		auto lhs = *cast(T *) p1, rhs = *cast(T *) p2;
211 		return (lhs > rhs) - (lhs < rhs);
212 		}
213         +/
214 
215 		static if (is(T == Base))
216 			override @property size_t tsize() nothrow pure
217 			{
218 				return T.sizeof;
219 			}
220 
221     static if (is(T == Base))
222         /+override @property size_t talign() nothrow pure
223         {
224 		return T.alignof;
225         }+/
226 
227 		// Override initializer only if necessary.
228 		static if (is(T == Base) || T.init != Base.init)
229 			override const(void)[] initializer() @trusted
230 			{
231 				static if (__traits(isZeroInit, T))
232 				{
233 					return (cast(void *)null)[0 .. T.sizeof];
234 				}
235 				else
236 				{
237 					static immutable T[1] c;
238 					return c;
239 				}
240 			}
241 
242     // `swap` is the same for `Base` and `T`, so introduce only once.
243     static if (is(T == Base))
244         /+override void swap(void *p1, void *p2)
245         {
246 		auto t = *cast(T *) p1;
247 		*cast(T *)p1 = *cast(T *)p2;
248 		*cast(T *)p2 = t;
249         }+/
250 
251 		static if (is(T == Base) || RTInfo!T != RTInfo!Base)
252 			/+override @property immutable(void)* rtInfo() nothrow pure const @safe
253 			{
254             return RTInfo!T;
255 			}+/
256 
257 			static if (is(T == Base))
258 			{
259 				/+static if ((__traits(isFloating, T) && T.mant_dig != 64) ||
260 				(isComplex!T && T.re.mant_dig != 64))
261 				// FP types except 80-bit X87 are passed in SIMD register.
262 				override @property uint flags() const { return 2; }+/
263 			}
264 }
265 
266 
267 /*
268 TypeInfo information for arrays of built-in types.
269 A `Base` type may be specified, which must be a type with the same layout, alignment, hashing, and
270 equality comparison as type `T`. This saves on code size because parts of `Base` will be reused. Example:
271 `char` and `ubyte`. The implementation assumes `Base` and `T` hash the same, swap
272 the same, have the same ABI flags, and compare the same for equality. For ordering comparisons, we detect
273 during compilation whether they have different signedness and override appropriately. For initializer, we
274 detect if we need to override. The overriding initializer should be nonzero.
275 */
276 version(LWDR_DynamicArray)
277 private class TypeInfoArrayGeneric(T, Base = T) : Select!(is(T == Base), TypeInfo_Array, TypeInfoArrayGeneric!Base)
278 {
279     static if (is(T == Base))
280         override bool opEquals(Object o) { return TypeInfo.opEquals(o); }
281 
282     override string toString() const { return (T[]).stringof; }
283 
284     /+static if (is(T == Base)) {
285 	override size_t getHash(scope const void* p) @trusted const
286 	{
287 	return 0;
288 	}
289     }+/
290 
291     static if (is(T == Base)) {
292         override bool equals(in void* p1, in void* p2) const
293         {
294             static if (__traits(isFloating, T) || isComplex!T)
295             {
296                 return Array!T.equals(*cast(T[]*)p1, *cast(T[]*)p2);
297             }
298             else
299             {
300                 import core.stdc..string;
301                 auto s1 = *cast(T[]*)p1;
302                 auto s2 = *cast(T[]*)p2;
303                 return s1.length == s2.length &&
304                     memcmp(s1.ptr, s2.ptr, s1.length) == 0;
305             }
306         }
307     }
308 
309     static if (is(T == Base) || (__traits(isIntegral, T) && T.max != Base.max)) {
310         /+override int compare(in void* p1, in void* p2) const
311         {
312 		static if (__traits(isFloating, T) || isComplex!T)
313 		{
314 		return Array!T.compare(*cast(T[]*)p1, *cast(T[]*)p2);
315 		}
316 		else
317 		{
318 		auto s1 = *cast(T[]*)p1;
319 		auto s2 = *cast(T[]*)p2;
320 		auto len = s1.length;
321 
322 		if (s2.length < len)
323 		len = s2.length;
324 		for (size_t u = 0; u < len; u++)
325 		{
326 		if (int result = (s1[u] > s2[u]) - (s1[u] < s2[u]))
327 		return result;
328 		}
329 		return (s1.length > s2.length) - (s1.length < s2.length);
330 		}
331         }+/
332 	}
333 
334     version(LWDR_INTERNAL_ti_next)
335     override @property inout(TypeInfo) next() inout
336     {
337         return cast(inout) typeid(T);
338     }
339 }
340 
341 // void
342 class TypeInfo_v : TypeInfoGeneric!ubyte
343 {
344 const: nothrow: pure: @trusted:
345 
346     override string toString() const pure nothrow @safe { return "void"; }
347 
348     /+override size_t getHash(scope const void* p)
349     {
350 	assert(0);
351     }
352 
353     override @property uint flags() nothrow pure
354     {
355 	return 1;
356     }
357 
358     unittest
359     {
360 	assert(typeid(void).toString == "void");
361 	assert(typeid(void).flags == 1);
362     }+/
363 }
364 
365 class TypeInfo_h : TypeInfoGeneric!ubyte {}
366 class TypeInfo_b : TypeInfoGeneric!(bool, ubyte) {}
367 class TypeInfo_g : TypeInfoGeneric!(byte, ubyte) {}
368 class TypeInfo_a : TypeInfoGeneric!(char, ubyte) {}
369 class TypeInfo_t : TypeInfoGeneric!ushort {}
370 class TypeInfo_s : TypeInfoGeneric!(short, ushort) {}
371 class TypeInfo_u : TypeInfoGeneric!(wchar, ushort) {}
372 class TypeInfo_w : TypeInfoGeneric!(dchar, uint) {}
373 class TypeInfo_k : TypeInfoGeneric!uint {}
374 class TypeInfo_i : TypeInfoGeneric!(int, uint) {}
375 class TypeInfo_m : TypeInfoGeneric!ulong {}
376 class TypeInfo_l : TypeInfoGeneric!(long, ulong) {}
377 static if (is(cent)) class TypeInfo_zi : TypeInfoGeneric!cent {}
378 static if (is(ucent)) class TypeInfo_zk : TypeInfoGeneric!ucent {}
379 
380 // All simple floating-point types.
381 class TypeInfo_f : TypeInfoGeneric!float {}
382 class TypeInfo_d : TypeInfoGeneric!double {}
383 class TypeInfo_e : TypeInfoGeneric!real {}
384 
385 version(LWDR_DynamicArray) {
386 
387 class TypeInfo_Ah : TypeInfoArrayGeneric!ubyte {}
388 class TypeInfo_Ab : TypeInfoArrayGeneric!(bool, ubyte) {}
389 class TypeInfo_Ag : TypeInfoArrayGeneric!(byte, ubyte) {}
390 class TypeInfo_Aa : TypeInfoArrayGeneric!(char, ubyte) {}
391 class TypeInfo_Axa : TypeInfoArrayGeneric!(const char) {}
392 class TypeInfo_Aya : TypeInfoArrayGeneric!(immutable char)
393 {
394     // Must override this, otherwise "string" is returned.
395     override string toString() const { return "immutable(char)[]"; }
396 }
397 class TypeInfo_At : TypeInfoArrayGeneric!ushort {}
398 class TypeInfo_As : TypeInfoArrayGeneric!(short, ushort) {}
399 class TypeInfo_Au : TypeInfoArrayGeneric!(wchar, ushort) {}
400 class TypeInfo_Ak : TypeInfoArrayGeneric!uint {}
401 class TypeInfo_Ai : TypeInfoArrayGeneric!(int, uint) {}
402 class TypeInfo_Aw : TypeInfoArrayGeneric!(dchar, uint) {}
403 class TypeInfo_Am : TypeInfoArrayGeneric!ulong {}
404 class TypeInfo_Al : TypeInfoArrayGeneric!(long, ulong) {}
405 
406 // Arrays of all simple floating-point types.
407 class TypeInfo_Af : TypeInfoArrayGeneric!float {}
408 class TypeInfo_Ad : TypeInfoArrayGeneric!double {}
409 class TypeInfo_Ae : TypeInfoArrayGeneric!real {}
410 }
411 
412 // typeof(null)
413 class TypeInfo_n : TypeInfo
414 {
415     override string toString() const @safe { return "typeof(null)"; }
416 
417     /+override size_t getHash(scope const void* p) const
418     {
419 	return 0;
420     }+/
421 
422     override bool equals(in void* p1, in void* p2) const @trusted
423     {
424         return true;
425     }
426 
427     /+override int compare(in void* p1, in void* p2) const @trusted
428     {
429 	return 0;
430     }+/
431 
432     override @property size_t tsize() const
433     {
434         return typeof(null).sizeof;
435     }
436 
437     override const(void)[] initializer() const @trusted
438     {
439         __gshared immutable void[typeof(null).sizeof] init;
440         return init;
441     }
442 
443     /+override void swap(void *p1, void *p2) const @trusted
444     {
445     }+/
446 
447     //override @property immutable(void)* rtInfo() nothrow pure const @safe { return rtinfoNoPointers; }
448 
449     unittest
450     {
451         with (typeid(typeof(null)))
452         {
453             assert(toString == "typeof(null)");
454             assert(getHash(null) == 0);
455             assert(equals(null, null));
456             assert(compare(null, null) == 0);
457             assert(tsize == typeof(null).sizeof);
458             assert(initializer == new ubyte[(void*).sizeof]);
459             assert(rtInfo == rtinfoNoPointers);
460         }
461     }
462 }
463 
464 version(LWDR_DynamicArray) {
465 // void[] is a bit different, behaves like ubyte[] for comparison purposes.
466 class TypeInfo_Av : TypeInfo_Ah
467 {
468     override string toString() const { return "void[]"; }
469 
470     version(LWDR_INTERNAL_ti_next)
471     override @property inout(TypeInfo) next() inout
472     {
473         return cast(inout) typeid(void);
474     }
475 
476     unittest
477     {
478         assert(typeid(void[]).toString == "void[]");
479         assert(typeid(void[]).next == typeid(void));
480     }
481 }
482 }