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 }