1 /++
2 Conversion utilities.
3 
4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
5 Authors: Ilya Yaroshenko
6 +/
7 module mir.conv;
8 
9 public import core.lifetime: emplace;
10 
11 import std.traits;
12 
13 private template isMirString(T)
14 {
15     static if (isSomeString!T)
16     {
17         enum isMirString = true;
18     }
19     else
20     {
21         static if (__traits(compiles, {import mir.small_string;}))
22         {
23             import mir.small_string;
24             enum isMirString = is(T : SmallString!size, size_t size);
25         }
26         else
27         {
28             enum isMirString = false;
29         }
30     }
31 }
32 
33 /++
34 The `to` template converts a value from one type _to another.
35 The source type is deduced and the target type must be specified, for example the
36 expression `to!int(42.0)` converts the number 42 from
37 `double` _to `int`. The conversion is "unsafe", i.e.,
38 it does not check for overflow.
39 +/
40 template to(T)
41 {
42     ///
43     auto ref T to(A...)(auto ref A args)
44         if (A.length > 0)
45     {
46         import mir.utility;
47         import mir.functional: forward;
48         static if (A.length == 1 && isImplicitlyConvertible!(A[0], T))
49             return args[0];
50         else
51         static if (is(T == class) && is(typeof(new T(forward!args))))
52             return new T(forward!args);
53         else
54         static if (is(typeof(T(args))))
55             return T(forward!args);
56         else
57         static if (A.length == 1)
58         {
59             alias I = A[0];
60             alias arg = args[0];
61             static if (is(typeof(cast(T) arg)) && !(isDynamicArray!T && isDynamicArray!I) && !isSomeString!T)
62                 return cast(T) forward!arg;
63             else
64             static if (isSomeString!I && is(T == enum))
65             {
66                 import mir.enums;
67                 uint index = void;
68                 if (getEnumIndexFromKey!T(arg, index)._expect(true))
69                     return index.unsafeEnumFromIndex!T;
70                 static immutable msg = "Can not convert string to the enum " ~ T.stringof;
71                 version (D_Exceptions)
72                 {
73                     static immutable Exception exc = new Exception(msg);
74                     throw exc;
75                 }
76                 else
77                 {
78                     assert(0, msg);
79                 }
80             }
81             else
82             static if (is(I == enum) && isSomeString!T)
83             {
84                 import mir.enums;
85                 uint id = void;
86                 if (getEnumIndex(arg, id)._expect(true))
87                     return enumStrings!I[id];
88                 assert(0);
89             }
90             else
91             static if (isMirString!I && !isSomeString!T)
92             {
93                 static assert (__traits(compiles, { import mir.parse: fromString; }));
94                 import mir.parse: fromString;
95                 return fromString!(Unqual!T)(arg[]);
96             }
97             else
98             static if (!isSomeString!I && isMirString!T)
99             {
100                 // static if (is(Unqual!I == typeof(null)))
101                 // {
102                 //     enum immutable(T) ret = "null";
103                 //     static if (isImplicitlyConvertible!(immutable T, T))
104                 //         return ret;
105                 //     else
106                 //         return .to!T(ret[]);
107                 // }
108                 // else
109                 static if (is(Unqual!I == bool))
110                 {
111                     enum immutable(T) t = "true";
112                     enum immutable(T) f = "false";
113                     auto ret = arg ? t : f;
114                     static if (isImplicitlyConvertible!(immutable T, T))
115                         return ret;
116                     else
117                         return .to!T(ret[]);
118                 }
119                 else
120                 {
121                     static if (isImplicitlyConvertible!(T, string) && __traits(compiles, () @safe {const(char)[] s =  arg.toString; return s;}))
122                     {
123                         auto ret = arg.toString;
124                         static if (is(typeof(ret) : string))
125                             return ret;
126                         else
127                             return ret.idup;
128                     }
129                     else
130                     {
131                         static assert (__traits(compiles, { import mir.format: print; }));
132                         import mir.format: print;
133                         static if (isSomeString!T)
134                         {
135                             static if (isNumeric!I)
136                             {
137                                 import mir.appender: UnsafeArrayBuffer;
138                                 alias C = Unqual!(ForeachType!T);
139                                 C[64] array = '\0';
140                                 auto buffer = UnsafeArrayBuffer!C(array);
141                             }
142                             else
143                             {
144                                 import mir.appender: ScopedBuffer;
145                                 ScopedBuffer!(Unqual!(ForeachType!T)) buffer;
146                             }
147                             buffer.print(arg);
148                             static if (isMutable!(ForeachType!(T)))
149                                 return buffer.data.dup;
150                             else
151                                 return buffer.data.idup;
152                         }
153                         else
154                         {
155                             Unqual!T buffer;
156                             buffer.print(arg);
157                             return buffer;
158                         }
159                     }
160                 }
161             }
162             else
163             static if (is(I : const(C)[], C) && is(T : immutable(C)[]))
164             {
165                 static if (is(I : immutable(C)[]))
166                     return arg;
167                 else
168                     return idup(arg);
169             }
170             else
171             static if (is(I : const(D)[], D) && is(T : D[]))
172             {
173                 static if (is(I : D[]))
174                     return arg;
175                 else
176                     return dup(arg);
177             }
178             else
179                 static assert(0);
180         }
181         else
182             static assert(0);
183     }
184 }
185 
186 ///
187 @safe pure @nogc
188 version(mir_core_test) unittest
189 {
190     enum E
191     {
192         A,
193         B,
194         C,
195     }
196 
197     assert(to!E("B") == E.B);
198     assert(to!string(E.B) == "B");
199     assert(to!string(null) is null);
200     assert(to!string(true) == "true");
201     assert(to!string(false) == "false");
202 
203     enum S : wstring
204     {
205         a = "A",
206         b = "B",
207     }
208 
209     assert(to!wstring(S.b) == "B"w);
210     assert(to!S("B"w) == S.b);
211 }
212 
213 /++
214 Emplace helper function.
215 +/
216 void emplaceInitializer(T)(scope ref T chunk) @trusted pure nothrow
217 
218 {
219     // Emplace T.init.
220     // Previously, an immutable static and memcpy were used to hold an initializer.
221     // With improved unions, this is no longer needed.
222     union UntypedInit
223     {
224         T dummy;
225     }
226     static struct UntypedStorage
227     {
228         align(T.alignof) void[T.sizeof] dummy;
229     }
230 
231     () @trusted {
232         *cast(UntypedStorage*) &chunk = cast(UntypedStorage) UntypedInit.init;
233     } ();
234 }
235 
236 /++
237 +/
238 T[] uninitializedFillDefault(T)(return scope T[] array) nothrow @nogc
239 {
240     static if (__VERSION__ < 2083)
241     {
242         static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1))
243         {
244             import core.stdc..string : memset;
245             memset(array.ptr, 0xff, T.sizeof * array.length);
246             return array;
247         }
248         else
249         {
250             pragma(inline, false);
251             foreach(ref e; array)
252                 emplaceInitializer(e);
253             return array;
254         }
255     }
256     else
257     {
258         static if (__traits(isZeroInit, T))
259         {
260             import core.stdc..string : memset;
261             memset(array.ptr, 0, T.sizeof * array.length);
262             return array;
263         }
264         else static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1))
265         {
266             import core.stdc..string : memset;
267             memset(array.ptr, 0xff, T.sizeof * array.length);
268             return array;
269         }
270         else
271         {
272             pragma(inline, false);
273             foreach(ref e; array)
274                 emplaceInitializer(e);
275             return array;
276         }
277     }
278 }
279 
280 ///
281 pure nothrow @nogc @system
282 version(mir_core_test) unittest
283 {
284     static struct S { int x = 42; @disable this(this); }
285 
286     int[5] expected = [42, 42, 42, 42, 42];
287     S[5] arr = void;
288     uninitializedFillDefault(arr);
289     assert((cast(int*) arr.ptr)[0 .. arr.length] == expected);
290 }
291 
292 ///
293 @system 
294 version(mir_core_test) unittest
295 {
296     int[] a = [1, 2, 4];
297     uninitializedFillDefault(a);
298     assert(a == [0, 0, 0]);
299 }
300 
301 /++
302 Destroy structs and unions usnig `__xdtor` member if any.
303 Do nothing for other types.
304 +/
305 void xdestroy(T)(scope T[] ar)
306 {
307     static if ((is(T == struct) || is(T == union)) && __traits(hasMember, T, "__xdtor"))
308     {
309         static if (__traits(isSame, T, __traits(parent, ar[0].__xdtor)))
310         {
311             pragma(inline, false);
312             foreach_reverse (ref e; ar)
313                 e.__xdtor();
314         }
315     }
316 }
317 
318 ///
319 nothrow @nogc version(mir_core_test) unittest
320 {
321     __gshared int d;
322     __gshared int c;
323     struct D { ~this() nothrow @nogc {d++;} }
324     extern(C++)
325     struct C { ~this() nothrow @nogc {c++;} }
326     C[2] carray;
327     D[2] darray;
328     carray.xdestroy;
329     darray.xdestroy;
330     assert(c == 2);
331     assert(d == 2);
332     c = 0;
333     d = 0;
334 }
335 
336 
337 template emplaceRef(T)
338 {
339     void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
340     {
341         import core.lifetime: forward;
342         import core.internal.lifetime: emplaceRef;
343         return emplaceRef!T(chunk, forward!args);
344     }
345 }
346 
347 void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
348 if (is(UT == Unqual!UT))
349 {
350     import core.lifetime: forward;
351     emplaceRef!UT(chunk, forward!args);
352 }