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.analyze.id_factory; 11 12 import logger = std.experimental.logger; 13 14 import dextool.type : Path; 15 16 import dextool.plugin.mutate.backend.analyze.extensions; 17 import dextool.plugin.mutate.backend.analyze.internal; 18 19 @safe: 20 21 /** Create mutation ID's from source code mutations. 22 * 23 * Note that the checksum is only based on the content and how it is changed. 24 * The filename is not part of the checksum. This mean that the checksum will 25 * "detect and reattach" to a file after a rename. 26 * 27 * The algorithm is a checksum of: 28 * * the content of all relevant tokens, e.g. all except comments 29 * * the token position before and after the mutant. 30 * * the original text 31 * * the mutated text 32 */ 33 struct MutationIdFactory { 34 import my.hash : Checksum128, BuildChecksum128, toBytes, toChecksum128; 35 import dextool.plugin.mutate.backend.type : CodeMutant, CodeChecksum, Mutation, Checksum; 36 import dextool.type : Path; 37 38 private { 39 /// Checksum of the filename containing the mutants. 40 Checksum file; 41 /// Checksum of all tokens content. 42 Checksum content; 43 44 /// Where in the token stream the preMutant calculation is. 45 size_t preIdx; 46 Checksum preMutant; 47 /// Where in the post tokens the postMutant is. 48 size_t postIdx; 49 Checksum postMutant; 50 } 51 52 /** 53 * Params: 54 * filename = the file that the factory is for 55 * file = checksum of the filename. 56 * tokens = all tokens from the file. 57 */ 58 this(Path fileName, Token[] tokens) { 59 file = () { 60 BuildChecksum128 bc; 61 bc.put(cast(const(ubyte)[]) fileName.toString); 62 return toChecksum128(bc); 63 }(); 64 65 content = () { 66 BuildChecksum128 bc; 67 foreach (t; tokens) { 68 bc.put(cast(const(ubyte)[]) t.spelling); 69 } 70 return toChecksum128(bc); 71 }(); 72 } 73 74 /// Update the number of tokens that are before and after the mutant. 75 void updatePosition(const size_t preCnt, const size_t postCnt) { 76 // only do it if the position changes 77 if (preCnt == preIdx && postCnt == postIdx) 78 return; 79 80 preIdx = preCnt; 81 postIdx = postCnt; 82 83 { 84 BuildChecksum128 bc; 85 bc.put(preIdx.toBytes); 86 preMutant = toChecksum128(bc); 87 } 88 { 89 BuildChecksum128 bc; 90 bc.put(postIdx.toBytes); 91 postMutant = toChecksum128(bc); 92 } 93 } 94 95 /// Calculate the unique ID for a specific mutation at this point. 96 Checksum128 makeId(const(ubyte)[] mut) @safe pure nothrow const @nogc scope { 97 BuildChecksum128 h; 98 99 h.put(file.c0.toBytes); 100 h.put(file.c1.toBytes); 101 102 h.put(content.c0.toBytes); 103 h.put(content.c1.toBytes); 104 105 h.put(preMutant.c0.toBytes); 106 h.put(preMutant.c1.toBytes); 107 108 h.put(mut); 109 110 h.put(postMutant.c0.toBytes); 111 h.put(postMutant.c1.toBytes); 112 return toChecksum128(h); 113 } 114 115 /// Create a mutant at this mutation point. 116 CodeMutant makeMutant(Mutation m, const(ubyte)[] mut) @safe pure nothrow const @nogc scope { 117 auto id = makeId(mut); 118 return CodeMutant(CodeChecksum(id), m); 119 } 120 }