1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module dextool.plugin.mutate.backend.utility;
11 
12 import core.time : Duration;
13 import std.algorithm : filter;
14 import std.typecons : Flag, No;
15 
16 import dextool.type : Path, AbsolutePath;
17 import dextool.from;
18 
19 public import dextool.plugin.mutate.backend.type;
20 public import dextool.plugin.mutate.backend.mutation_type;
21 public import dextool.clang_extensions : OpKind;
22 public import dextool.plugin.mutate.backend.interface_ : Blob;
23 
24 @safe:
25 
26 Path trustedRelativePath(string p, AbsolutePath root) @trusted {
27     import std.path : relativePath;
28 
29     return relativePath(p, root).Path;
30 }
31 
32 /**
33  * trusted: void[] is perfectly representable as ubyte[] accoding to the specification.
34  */
35 Checksum checksum(const(ubyte)[] a) {
36     import dextool.hash : makeMurmur3;
37 
38     return makeMurmur3(a);
39 }
40 
41 /// Package the values to a checksum.
42 Checksum checksum(T)(const(T[2]) a) if (T.sizeof == 8) {
43     return Checksum(cast(ulong) a[0], cast(ulong) a[1]);
44 }
45 
46 /// Package the values to a checksum.
47 Checksum checksum(T)(const T a, const T b) if (T.sizeof == 8) {
48     return Checksum(cast(ulong) a, cast(ulong) b);
49 }
50 
51 /// Sleep for a random time that is min_ + rnd(0, span msecs)
52 void rndSleep(Duration min_, int span) nothrow @trusted {
53     import core.thread : Thread;
54     import core.time : dur;
55     import std.random : uniform;
56 
57     auto t_span = () {
58         try {
59             return uniform(0, span).dur!"msecs";
60         } catch (Exception e) {
61         }
62         return span.dur!"msecs";
63     }();
64 
65     Thread.sleep(min_ + t_span);
66 }
67 
68 /** Returns: the file content as an array of tokens.
69  *
70  * This is a bit slow, I think. Optimize by reducing the created strings.
71  * trusted: none of the unsafe accessed data escape this function.
72  *
73  * Params:
74  *  splitMultiLineTokens = a token, mostly comment tokens, can be over multiple
75  *      lines. If true then this split it into multiple tokens where a token is at
76  *      most one per line.
77  */
78 auto tokenize(Flag!"splitMultiLineTokens" splitTokens = No.splitMultiLineTokens)(
79         ref from.cpptooling.analyzer.clang.context.ClangContext ctx, Path file) @trusted {
80     import std.algorithm : splitter;
81     import std.array : appender;
82     import std.range : enumerate;
83 
84     auto tu = ctx.makeTranslationUnit(file);
85     auto toks = appender!(Token[])();
86     foreach (ref t; tu.cursor.tokens) {
87         const ext = t.extent;
88         const start = ext.start;
89         const end = ext.end;
90         const spell = t.spelling;
91 
92         static if (splitTokens) {
93             // TODO: this do not correctly count the utf-8 graphems but rather
94             // the code points because `.length` is used.
95 
96             auto offset = Offset(start.offset, start.offset);
97             auto startLoc = SourceLoc(start.line, start.column);
98             auto endLoc = startLoc;
99             foreach (ts; spell.splitter('\n').enumerate) {
100                 offset = Offset(offset.end, cast(uint)(offset.end + ts.length));
101 
102                 if (ts.index == 0) {
103                     endLoc = SourceLoc(start.line, cast(uint)(start.column + ts.value.length));
104                 } else {
105                     startLoc = SourceLoc(startLoc.line + 1, 1);
106                     endLoc = SourceLoc(startLoc.line, cast(uint) ts.value.length);
107                 }
108 
109                 toks.put(Token(t.kind, offset, startLoc, endLoc, ts.value));
110             }
111         } else {
112             auto offset = Offset(start.offset, end.offset);
113             auto startLoc = SourceLoc(start.line, start.column);
114             auto endLoc = SourceLoc(end.line, end.column);
115             toks.put(Token(t.kind, offset, startLoc, endLoc, spell));
116         }
117     }
118 
119     return toks.data;
120 }
121 
122 struct TokenRange {
123     private {
124         Token[] tokens;
125     }
126 
127     Token front() @safe pure nothrow {
128         assert(!empty, "Can't get front of an empty range");
129         return tokens[0];
130     }
131 
132     void popFront() @safe pure nothrow {
133         assert(!empty, "Can't pop front of an empty range");
134         tokens = tokens[1 .. $];
135     }
136 
137     bool empty() @safe pure nothrow const @nogc {
138         return tokens.length == 0;
139     }
140 }