package net.mcforkage.ant; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; public class CreateDiff2Task extends Task { private File from, to, output; public void setFrom(File f) {from = f;} public void setTo(File f) {to = f;} public void setOutput(File f) {output = f;} public static void main(String[] args) { CreateDiff2Task t = new CreateDiff2Task(); t.from = new File("../../build/bytecode-old.txt"); t.to = new File("../../build/bytecode-new.txt"); t.output = new File("../../build/bytecode.patch2"); t.execute(); } private static class SearchResult { public int index, length; public SearchResult(int index, int length) { this.index = index; this.length = length; } } private static class IntCorpusIndex { private int[] haystack; private Map<Integer, int[]> indices = new HashMap<Integer, int[]>(); private Map<Long, int[]> dual_indices = new HashMap<Long, int[]>(); public IntCorpusIndex(int[] haystack) { this.haystack = haystack; Map<Integer, List<Integer>> indices_temp = new HashMap<>(); Map<Long, List<Integer>> dual_indices_temp = new HashMap<>(); for(int index = 0; index < haystack.length; index++) { putToMultimap(indices_temp, haystack[index], index); if(index > 0) putToMultimap(dual_indices_temp, ((long)haystack[index] << 32) | (haystack[index-1] & 0xFFFFFFFFL), index-1); } for(Map.Entry<Integer, List<Integer>> entry : indices_temp.entrySet()) { indices.put(entry.getKey(), toIntArray(entry.getValue())); } for(Map.Entry<Long, List<Integer>> entry : dual_indices_temp.entrySet()) { dual_indices.put(entry.getKey(), toIntArray(entry.getValue())); } } private static <K, V> void putToMultimap(Map<K, List<V>> map, K key, V value) { if(!map.containsKey(key)) map.put(key, new ArrayList<V>()); map.get(key).add(value); } private static int[] toIntArray(List<Integer> l) { int[] ints = new int[l.size()]; for(int k = 0; k < ints.length; k++) ints[k] = l.get(k); return ints; } public SearchResult search(int[] needle, int needleStart) { int[] indicesToTry; int best_index = 0; int best_length = 0; indicesToTry = indices.get(needle[needleStart]); if(indicesToTry == null) return null; best_index = indicesToTry[0]; best_length = 1; indicesToTry = dual_indices.get(((long)needle[needleStart+1] << 32) | (needle[needleStart] & 0xFFFFFFFFL)); if(indicesToTry != null) { for(int k : indicesToTry) { if(k + best_length >= haystack.length) continue; if(haystack[k+best_length-1] != needle[needleStart+best_length-1]) continue; int i = 0; for(i = 0; i < needle.length - needleStart; i++) { if(haystack[k+i] != needle[needleStart+i]) break; } if(i > best_length) { best_length = i; best_index = k; } } } return new SearchResult(best_index, best_length); } } private static boolean arrayContains(int[] a, int b) { for(int c : a) if(b == c) return true; return false; } @Override public void execute() throws BuildException { if(from == null) throw new BuildException("From file not set"); if(to == null) throw new BuildException("To file not set"); if(output == null) throw new BuildException("Output file not set"); List<String> fromLines = readFile(from); List<String> toLines = readFile(to); Map<String, Integer> lineToIndex = new HashMap<String, Integer>(); Map<Integer, String> indexToLine = new HashMap<Integer, String>(); int[] fromLineIndices = linesToIndices(fromLines, lineToIndex, indexToLine); int[] toLineIndices = linesToIndices(toLines, lineToIndex, indexToLine); IntCorpusIndex fromLineIndicesIndex = new IntCorpusIndex(fromLineIndices); toLines = null; fromLines = null; try (PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(output), StandardCharsets.UTF_8))) { int nextToLine = 0; int progressCounter = 0; while(nextToLine < toLineIndices.length) { SearchResult result = fromLineIndicesIndex.search(toLineIndices, nextToLine); if(result == null) { while(nextToLine < toLineIndices.length && !arrayContains(fromLineIndices, toLineIndices[nextToLine])) { out.println("write " + indexToLine.get(toLineIndices[nextToLine])); nextToLine++; } } else { out.println("copy " + result.index + " " + result.length); nextToLine += result.length; } if((++progressCounter) % 10000 == 0) System.out.println(nextToLine+" / "+toLineIndices.length); } } catch(IOException e) { throw new BuildException(e); } } private int[] linesToIndices(List<String> lines, Map<String, Integer> lineToIndex, Map<Integer, String> indexToLine) { int[] a = new int[lines.size()]; for(int k = 0; k < a.length; k++) { Integer index = lineToIndex.get(lines.get(k)); if(index == null) { index = indexToLine.size(); indexToLine.put(index, lines.get(k)); lineToIndex.put(lines.get(k), index); } a[k] = index; } return a; } private List<String> readFile(File f) throws BuildException { ArrayList<String> lines = new ArrayList<>(); try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8))) { String line; while((line = in.readLine()) != null) { if(line.endsWith("\r")) throw new BuildException("x"); lines.add(line); } } catch(IOException e) { throw new BuildException("Error reading "+f, e); } lines.trimToSize(); return lines; } }