1 /// Basic functions used in the tagion project
2 module tagion.basic.basic;
3 
4 private import std.string : join;
5 private import std.traits;
6 import std.meta : AliasSeq;
7 
8 enum this_dot = "this.";
9 import std.conv;
10 
11 /++
12  Returns:
13  a immuatble do
14 +/
15 immutable(BUF) buf_idup(BUF)(immutable(Buffer) buffer) {
16     pragma(msg, "fixme(cbr): looks redundent");
17     return cast(BUF)(buffer.idup);
18 }
19 
20 template suffix(string name, size_t index) {
21     static if (index is 0) {
22         alias suffix = name;
23     }
24     else static if (name[index - 1]!is '.') {
25         alias suffix = suffix!(name, index - 1);
26     }
27     else {
28         enum cut_name = name[index .. $];
29         alias suffix = cut_name;
30     }
31 }
32 
33 /++
34  Template function returns the suffux name after the last '.'
35  +/
36 template basename(alias K) {
37     static if (is(K == string)) {
38         enum name = K;
39     }
40     else {
41         enum name = K.stringof;
42     }
43     enum basename = suffix!(name, name.length);
44 }
45 
46 enum NameOf(alias nameType) = __traits(identifier, nameType);
47 
48 /++
49  Returns:
50  function name of the current function
51 +/
52 mixin template FUNCTION_NAME() {
53     import tagion.basic.basic : basename;
54 
55     enum __FUNCTION_NAME__ = basename!(__FUNCTION__)[0 .. $ - 1];
56 }
57 
58 ///
59 unittest {
60     enum name_another = "another";
61     import std.algorithm.searching : countUntil;
62 
63     struct Something {
64         mixin("int " ~ name_another ~ ";");
65         void check() {
66             // Check that basename removes (this.) from the scope name space
67             static assert(this.another.stringof.countUntil('.') == this_dot.countUntil('.'));
68             static assert(basename!(this.another) == name_another);
69         }
70     }
71 
72     Something something;
73     // check that basename work in global scope (not this.)
74     static assert(something.stringof.countUntil('.') == -1);
75     static assert(basename!(something.another) == name_another);
76 }
77 /++
78  Builds and enum string out of a string array
79 +/
80 template EnumText(string name, string[] list, bool first = true) {
81     static if (first) {
82         enum begin = "enum " ~ name ~ "{";
83         alias EnumText = EnumText!(begin, list, false);
84     }
85     else static if (list.length > 0) {
86         enum k = list[0];
87         enum code = name ~ k ~ " = " ~ '"' ~ k ~ '"' ~ ',';
88         alias EnumText = EnumText!(code, list[1 .. $], false);
89     }
90     else {
91         enum code = name ~ "}";
92         alias EnumText = code;
93     }
94 }
95 
96 ///
97 unittest {
98     enum list = ["red", "green", "blue"];
99     mixin(EnumText!("Colour", list));
100     static assert(Colour.red == list[0]);
101     static assert(Colour.green == list[1]);
102     static assert(Colour.blue == list[2]);
103 
104 }
105 
106 /++
107  Calculates log2
108  Returns:
109  log2(n)
110  +/
111 @trusted int log2(ulong n) {
112     if (n == 0) {
113         return -1;
114     }
115     import core.bitop : bsr;
116 
117     return bsr(n);
118 }
119 
120 ///
121 unittest {
122     // Undefined value returns -1
123     assert(log2(0) == -1);
124     assert(log2(17) == 4);
125     assert(log2(177) == 7);
126     assert(log2(0x1000_000_000) == 36);
127 
128 }
129 
130 /++
131  Generate a temporary file name
132 +/
133 @trusted
134 string tempfile() {
135     import std.file;
136     import std.path;
137     import std.random;
138     import std.range;
139 
140     auto rnd = Random(unpredictableSeed);
141     return buildPath(tempDir, generate!(() => uniform('A', 'Z', rnd)).takeExactly(20).array);
142 }
143 
144 @safe
145 void forceRemove(const(string) filename) {
146     import std.file : exists, remove;
147 
148     if (filename.exists) {
149         filename.remove;
150     }
151 }
152 
153 /++
154  Returns:
155  true if the type T is one of types in the list TList
156 +/
157 template isOneOf(T, TList...) {
158     static if (TList.length == 0) {
159         enum isOneOf = false;
160     }
161     else static if (is(T == TList[0])) {
162         enum isOneOf = true;
163     }
164     else {
165         alias isOneOf = isOneOf!(T, TList[1 .. $]);
166     }
167 }
168 
169 ///
170 static unittest {
171     import std.meta;
172 
173     alias Seq = AliasSeq!(long, int, ubyte);
174     static assert(isOneOf!(int, Seq));
175     static assert(!isOneOf!(double, Seq));
176 }
177 
178 /++
179    Finds the type in the TList which T can be typecast to
180    Returns:
181    void if not type is found
182  +/
183 template CastTo(T, TList...) {
184     static if (TList.length is 0) {
185         alias CastTo = void;
186     }
187     else {
188         alias castT = TList[0];
189         static if (is(T : castT)) {
190             alias CastTo = castT;
191         }
192         else {
193             alias CastTo = CastTo!(T, TList[1 .. $]);
194         }
195     }
196 }
197 
198 ///
199 static unittest {
200     static assert(is(void == CastTo!(string, AliasSeq!(int, long, double))));
201     static assert(is(double == CastTo!(float, AliasSeq!(int, long, double))));
202     static assert(is(string == CastTo!(string, AliasSeq!(uint, string))));
203     static assert(is(uint == CastTo!(ushort, AliasSeq!(uint, string))));
204     static assert(is(uint == CastTo!(int, AliasSeq!(string, uint))));
205     static assert(is(const(uint) == CastTo!(inout(uint), AliasSeq!(const(uint), const(string)))));
206 }
207 
208 import std.typecons : Tuple;
209 
210 alias FileNames = Tuple!(string, "tempdir", string, "filename", string, "fullpath");
211 const(FileNames) fileId(T)(string ext, string prefix = null) @safe {
212     import std.array : join;
213     import std.file;
214     import std.path;
215     import std.process : environment, thisProcessID;
216 
217     //import std.traits;
218     FileNames names;
219     names.tempdir = tempDir.buildPath(environment.get("USER"));
220     names.filename = setExtension([prefix, thisProcessID.to!string, T.stringof].join("_"), ext);
221     names.fullpath = buildPath(names.tempdir, names.filename);
222     names.tempdir.exists || names.tempdir.mkdir;
223     return names;
224 }
225 
226 template EnumContinuousSequency(Enum) if (is(Enum == enum)) {
227     template Sequency(EList...) {
228         static if (EList.length is 1) {
229             enum Sequency = true;
230         }
231         else static if (EList[0] + 1 is EList[1]) {
232             enum Sequency = Sequency!(EList[1 .. $]);
233         }
234         else {
235             enum Sequency = false;
236         }
237     }
238 
239     enum EnumContinuousSequency = Sequency!(EnumMembers!Enum);
240 }
241 
242 ///
243 static unittest {
244     enum Count {
245         zero,
246         one,
247         two,
248         three
249     }
250 
251     static assert(EnumContinuousSequency!Count);
252 
253     enum NoCount {
254         zero,
255         one,
256         three = 3
257     }
258 
259     static assert(!EnumContinuousSequency!NoCount);
260 
261     enum OffsetCount {
262         one = 1,
263         two,
264         three
265     }
266 
267     static assert(EnumContinuousSequency!OffsetCount);
268 }
269 
270 /// isEqual is the same as `is()` function which can be used in template filters 
271 enum isEqual(T1, T2) = is(T1 == T2);
272 //enum isUnqualEqual(T1, T2) = is(Unqual!T1 == T2);
273 
274 unittest {
275     import std.meta : ApplyLeft, ApplyRight;
276     import std.traits : Unqual;
277 
278     static assert(isEqual!(int, int));
279     static assert(!isEqual!(int, long));
280     alias U = immutable(int);
281     static assert(isEqual!(int, Unqual!U));
282     alias Left = ApplyLeft!(isEqual, int);
283     static assert(Left!(Unqual!U));
284 }
285 
286 /// Calling any system functions.
287 template assumeTrusted(alias F) {
288     import std.traits;
289 
290     static assert(isUnsafe!F);
291 
292     auto assumeTrusted(Args...)(Args args) @trusted {
293         return F(args);
294     }
295 }
296 
297 ///
298 @safe
299 unittest {
300     auto bar(int b) @system {
301         return b + 1;
302     }
303 
304     const b = assumeTrusted!bar(5);
305     assert(b == 6);
306 
307     // applicable to 0-ary function
308     static auto foo() @system {
309         return 3;
310     }
311 
312     const a = assumeTrusted!foo;
313     assert(a == 3);
314 
315     // // It can be used for alias
316     alias trustedBar = assumeTrusted!bar;
317     alias trustedFoo = assumeTrusted!foo;
318     //    assert(is(typeof(trustedFoo) == function));
319 
320     import core.stdc.stdlib;
321 
322     auto ptr = assumeTrusted!malloc(100);
323     assert(ptr !is null);
324     ptr.assumeTrusted!free;
325 
326     ptr = assumeTrusted!calloc(10, 100);
327     ptr.assumeTrusted!free;
328 
329     alias lambda = assumeTrusted!((int a) @system => a * 3);
330 
331     assert(lambda(42) == 3 * 42);
332 
333     {
334         import std.concurrency;
335 
336         static void task() @safe {
337             const result = 2 * assumeTrusted!(receiveOnly!int);
338             assumeTrusted!({ ownerTid.send(result); });
339             alias trusted_owner = assumeTrusted!(ownerTid);
340             alias trusted_send = assumeTrusted!(send!(string));
341             trusted_send(trusted_owner, "Hello");
342         }
343 
344         auto tid = assumeTrusted!({ return spawn(&task); });
345         assumeTrusted!({ send(tid, 21); });
346         assert(assumeTrusted!(receiveOnly!(const(int))) == 21 * 2);
347         assert(assumeTrusted!(receiveOnly!(string)) == "Hello");
348     }
349 }
350 
351 protected template _staticSearchIndexOf(int index, alias find, L...) {
352     import std.meta : staticIndexOf;
353 
354     static if (isType!find) {
355         enum _staticSearchIndexOf = staticIndexOf!(find, L);
356     }
357     else {
358         static if (L.length is index) {
359             enum _staticSearchIndexOf = -1;
360         }
361         else {
362             enum found = find!(L[index]);
363             static if (found) {
364                 enum _staticSearchIndexOf = index;
365             }
366             else {
367                 enum _staticSearchIndexOf = _staticSearchIndexOf!(index + 1, find, L);
368             }
369         }
370     }
371 }
372 
373 /**
374 This template finds the index of find in the AliasSeq L.
375 If find is a type it works the same as traits.staticIndexOf,
376  but if func is a templeate function it will use this function as a filter
377 Returns:
378 First index where find has been found
379 If nothing has been found the template returns -1
380  */
381 
382 template staticSearchIndexOf(alias find, L...) {
383     enum staticSearchIndexOf = _staticSearchIndexOf!(0, find, L);
384 }
385 
386 static unittest {
387     import std.traits : isFloatingPoint, isIntegral;
388 
389     alias seq = AliasSeq!(string, int, long, char);
390     static assert(staticSearchIndexOf!(long, seq) is 2);
391     static assert(staticSearchIndexOf!(isIntegral, seq) is 1);
392     static assert(staticSearchIndexOf!(isFloatingPoint, seq) is -1);
393 }
394 
395 enum unitdata = "unitdata";
396 
397 /**
398 * Used in unitttest local the path package/unitdata/filename 
399 * Params:
400 *   filename = name of the unitdata file
401 *   file = defailt location of the module
402 * Returns:
403 *   unittest data filename
404  */
405 string unitfile(string filename, string file = __FILE__) @safe {
406     import std.path;
407 
408     return buildPath(file.dirName, unitdata, filename);
409 }
410 
411 /** 
412  * Mangle of a callable symbol
413  * Params:
414  *   T = callable symbol 
415  * Returns:
416  *   mangle of callable T
417  */
418 template mangleFunc(alias T) if (isCallable!T) {
419     import core.demangle : mangle;
420 
421     alias mangleFunc = mangle!(FunctionTypeOf!(T));
422 }
423 
424 bool isinit(T)(T x) pure nothrow @safe {
425     return x is T.init;
426 }
427 
428 @safe
429 unittest {
430     class C {
431     }
432 
433     C c;
434     assert(c.isinit);
435     c = new C;
436     assert(!c.isinit);
437     static struct S {
438         int x;
439     }
440 
441     S s;
442     assert(s.isinit);
443     s = S(42);
444     assert(!s.isinit);
445 }
446 
447 auto trusted(alias func, Args...)(auto ref Args args) @trusted {
448     return func(args);
449 }
450 
451 auto scopedTrusted(alias func, Args...)(auto ref scope Args args) @trusted {
452     return func(args);
453 }
454 
455 @safe
456 unittest {
457     import std.traits;
458 
459     @system
460     int mul(int a, int b) {
461         return a * b;
462     }
463 
464     static assert(!isSafe!mul);
465     assert(trusted!mul(1, 2) == 2);
466     assert(scopedTrusted!mul(1, 2) == 2);
467 }