1 module exec.cache; 2 3 import exec.iexecprovider; 4 import std.typecons: Tuple; 5 import std.traits: ReturnType; 6 import std.algorithm: sort; 7 8 import vibe.core.log; 9 10 /++ 11 Execution provider that implements caching 12 for an allowed white list of source code files. 13 +/ 14 class Cache: IExecProvider 15 { 16 private IExecProvider realExecProvider_; 17 18 private alias ResultTuple = ReturnType!compileAndExecute; 19 private alias HashType = ReturnType!getSourceCodeHash; 20 private ResultTuple[HashType] sourceHashToOutput_; 21 private HashType[] allowedSources_; ///< Hash of allowed source code contents 22 23 /++ 24 Params: 25 realExecProvider = the execution provider if cache 26 can't give the answer 27 sourceCodeWhitelist = raw source code only allowed 28 to be cached 29 +/ 30 this(IExecProvider realExecProvider, 31 string[] sourceCodeWhitelist) 32 { 33 import std.algorithm: map; 34 import std.array: array; 35 this.realExecProvider_ = realExecProvider; 36 // allowedSources is no longer used 37 //this.allowedSources_ = sourceCodeWhitelist 38 //.map!(x => x.getSourceCodeHash) 39 //.array; 40 //sort(this.allowedSources_); 41 //assert(sourceCodeWhitelist.length == this.allowedSources_.length); 42 } 43 44 Tuple!(string, "output", bool, "success") compileAndExecute(RunInput input) 45 { 46 import std.range: assumeSorted; 47 import std.algorithm: min, canFind; 48 auto hash = getSourceCodeHash(input); 49 50 // allowedSources is no longer used 51 //if (!assumeSorted(this.allowedSources_).canFind(hash)) { 52 //auto result = realExecProvider_.compileAndExecute(input); 53 //return result; 54 if (auto cache = hash in sourceHashToOutput_) { 55 logInfo("Fetching '%s...' from cache", input.source[0 .. min($, 20)]); 56 return *cache; 57 } else { 58 auto result = realExecProvider_.compileAndExecute(input); 59 sourceHashToOutput_[hash] = result; 60 return result; 61 } 62 } 63 64 Package[] installedPackages() 65 { 66 return realExecProvider_.installedPackages; 67 } 68 } 69 70 private uint getSourceCodeHash(IExecProvider.RunInput input) 71 { 72 import std..string : representation; 73 import std.digest.crc : CRC32; 74 CRC32 crc; 75 crc.put(input.source.representation); 76 crc.put(input.compiler.representation); 77 crc.put(input.stdin.representation); 78 crc.put(input.args.representation); 79 crc.put(input.runtimeArgs.representation); 80 crc.put(input.color); 81 union view { 82 ubyte[4] source; 83 uint uint_; 84 } 85 view ret = cast(view) crc.finish; 86 return ret.uint_ ; 87 } 88 89 unittest 90 { 91 auto sourceCode1 = "test123"; 92 auto sourceCode2 = "void main() {}"; 93 auto sourceCode3 = "12838389349493"; 94 95 auto getSourceCodeHash2(string source) 96 { 97 IExecProvider.RunInput input = {source: source}; 98 return input; 99 } 100 101 import std.stdio: writeln; 102 auto hash1 = getSourceCodeHash2(sourceCode1); 103 writeln("hash1 = ", hash1); 104 auto hash2 = getSourceCodeHash2(sourceCode2); 105 writeln("hash2 = ", hash2); 106 auto hash3 = getSourceCodeHash2(sourceCode3); 107 writeln("hash3 = ", hash3); 108 109 assert(hash1 != hash2); 110 assert(hash2 != hash3); 111 112 // allowedSources is no longer used 113 //auto cache = new Cache(null, 114 //[ sourceCode1, sourceCode2, sourceCode3 ]); 115 //assert(cache.allowedSources_.length == 3); 116 //import std.algorithm: canFind; 117 //assert(cache.allowedSources_.canFind(hash1)); 118 //assert(cache.allowedSources_.canFind(hash2)); 119 //assert(cache.allowedSources_.canFind(hash3)); 120 } 121 122 // #668 - different compilers should not use the same hash 123 unittest 124 { 125 import std.stdio: writeln; 126 IExecProvider.RunInput input; 127 auto hash1 = getSourceCodeHash(input); 128 writeln("hash1 = ", hash1); 129 130 input.source = "aa"; 131 auto hash2 = getSourceCodeHash(input); 132 writeln("hash2 = ", hash2); 133 134 input.compiler = "ldc"; 135 auto hash3 = getSourceCodeHash(input); 136 writeln("hash3 = ", hash3); 137 138 assert(hash1 != hash2 && hash2 != hash3 && hash1 != hash3); 139 140 IExecProvider.RunInput input2 = { 141 source: "aa" 142 }; 143 auto hash4 = getSourceCodeHash(input2); 144 assert(hash4 == hash2); 145 }