Mercurial > hg4j
comparison test/org/tmatesoft/hg/test/TestRevlog.java @ 583:47dfa0ec7e35
Effective revlog patching
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 24 Apr 2013 15:39:53 +0200 |
parents | |
children | ed243b668502 |
comparison
equal
deleted
inserted
replaced
582:90df078d6418 | 583:47dfa0ec7e35 |
---|---|
1 /* | |
2 * Copyright (c) 2013 TMate Software Ltd | |
3 * | |
4 * This program is free software; you can redistribute it and/or modify | |
5 * it under the terms of the GNU General Public License as published by | |
6 * the Free Software Foundation; version 2 of the License. | |
7 * | |
8 * This program is distributed in the hope that it will be useful, | |
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 * GNU General Public License for more details. | |
12 * | |
13 * For information on how to redistribute this software under | |
14 * the terms of a license other than GNU General Public License | |
15 * contact TMate Software at support@hg4j.com | |
16 */ | |
17 package org.tmatesoft.hg.test; | |
18 | |
19 import java.io.ByteArrayOutputStream; | |
20 import java.io.File; | |
21 import java.io.IOException; | |
22 import java.nio.ByteBuffer; | |
23 import java.util.Arrays; | |
24 | |
25 import org.tmatesoft.hg.core.HgRepositoryNotFoundException; | |
26 import org.tmatesoft.hg.core.Nodeid; | |
27 import org.tmatesoft.hg.internal.ByteArrayDataAccess; | |
28 import org.tmatesoft.hg.internal.DiffHelper; | |
29 import org.tmatesoft.hg.internal.Patch; | |
30 import org.tmatesoft.hg.internal.DiffHelper.LineSequence; | |
31 import org.tmatesoft.hg.internal.RevlogDump.RevlogReader; | |
32 import org.tmatesoft.hg.repo.HgLookup; | |
33 import org.tmatesoft.hg.repo.HgManifest; | |
34 import org.tmatesoft.hg.repo.HgManifest.Flags; | |
35 import org.tmatesoft.hg.repo.HgRepository; | |
36 import org.tmatesoft.hg.util.Path; | |
37 | |
38 /** | |
39 * Not a real JUnit test now | |
40 * | |
41 * @author Artem Tikhomirov | |
42 * @author TMate Software Ltd. | |
43 */ | |
44 public class TestRevlog { | |
45 private ByteBuffer patchData; | |
46 | |
47 public static void main(String[] args) throws Exception { | |
48 File indexFile = new File("/home/artem/hg/cpython/.hg/store/00manifest.i"); | |
49 new TestRevlog().run(indexFile); | |
50 } | |
51 | |
52 private void run(File indexFile) throws Exception { | |
53 final boolean shallDumpDiff = Boolean.TRUE.booleanValue(); | |
54 final boolean thoroughCheck = Boolean.TRUE.booleanValue(); | |
55 // | |
56 RevlogReader rr = new RevlogReader(indexFile); | |
57 rr.init(true); | |
58 rr.needData(true); | |
59 int startEntryIndex = 76507; // 150--87 | |
60 rr.startFrom(startEntryIndex); | |
61 rr.readNext(); | |
62 ByteBuffer baseRevision = null; | |
63 if (rr.isPatch()) { | |
64 byte[] cc = getRevisionTrueContent(indexFile.getParentFile(), rr.entryIndex, rr.linkRevision); | |
65 baseRevision = ByteBuffer.wrap(cc); | |
66 } else { | |
67 baseRevision = ByteBuffer.allocate(rr.getDataLength()); | |
68 rr.getData(baseRevision); | |
69 baseRevision.flip(); | |
70 } | |
71 ByteArrayDataAccess baseRevisionContent = new ByteArrayDataAccess(baseRevision.array(), baseRevision.arrayOffset(), baseRevision.remaining()); | |
72 // | |
73 final long start = System.currentTimeMillis(); | |
74 int n = 1419; | |
75 Patch seqPatch = null, normalizedPatch = null; | |
76 while (rr.hasMore() && n-- > 0) { | |
77 rr.readNext(); | |
78 if (!rr.isPatch()) { | |
79 break; | |
80 } | |
81 if (rr.getDataLength() == 0) { | |
82 System.out.printf("Empty content of revision %d\n", rr.entryIndex); | |
83 continue; | |
84 } | |
85 Patch p1 = createPatch(rr); | |
86 if (seqPatch != null) { | |
87 if (n < 1) { | |
88 System.out.println("+" + p1); | |
89 System.currentTimeMillis(); | |
90 } | |
91 seqPatch = seqPatch.apply(p1); | |
92 Patch ppp = normalizedPatch.apply(p1); | |
93 normalizedPatch = ppp.normalize(); | |
94 if (n <= 1) { | |
95 System.out.println("=" + seqPatch); | |
96 } | |
97 if (n == 0) { | |
98 System.out.println("A" + ppp); | |
99 System.out.println("N" + normalizedPatch); | |
100 normalizedPatch = ppp; | |
101 } | |
102 // | |
103 if (!thoroughCheck) { | |
104 if (baseRevisionContent.length() + seqPatch.patchSizeDelta() != rr.actualLen) { | |
105 System.out.printf("Sequential patches:\tPatchRevision #%d (+%d, cset:%d) failed\n", rr.entryIndex, rr.entryIndex - startEntryIndex, rr.linkRevision); | |
106 } | |
107 if (baseRevisionContent.length() + normalizedPatch.patchSizeDelta() != rr.actualLen) { | |
108 System.out.printf("Normalized patches:\tPatchRevision #%d (+%d, cset:%d) failed\n", rr.entryIndex, rr.entryIndex - startEntryIndex, rr.linkRevision); | |
109 } | |
110 } else { | |
111 byte[] origin = getRevisionTrueContent(indexFile.getParentFile(), rr.entryIndex, rr.linkRevision); | |
112 try { | |
113 byte[] result1 = seqPatch.apply(baseRevisionContent, rr.actualLen); | |
114 if (!Arrays.equals(result1, origin)) { | |
115 System.out.printf("Sequential patches:\tPatchRevision #%d (+%d, cset:%d) failed\n", rr.entryIndex, rr.entryIndex - startEntryIndex, rr.linkRevision); | |
116 } | |
117 } catch (ArrayIndexOutOfBoundsException ex) { | |
118 System.err.printf("Failure at entry %d (+%d)\n", rr.entryIndex, rr.entryIndex - startEntryIndex); | |
119 ex.printStackTrace(); | |
120 } | |
121 try { | |
122 byte[] result2 = normalizedPatch.apply(baseRevisionContent, rr.actualLen); | |
123 if (!Arrays.equals(result2, origin)) { | |
124 System.out.printf("Normalized patches:\tPatchRevision #%d (+%d, cset:%d) failed\n", rr.entryIndex, rr.entryIndex - startEntryIndex, rr.linkRevision); | |
125 } | |
126 } catch (ArrayIndexOutOfBoundsException ex) { | |
127 System.err.printf("Failure at entry %d (+%d)\n", rr.entryIndex, rr.entryIndex - startEntryIndex); | |
128 ex.printStackTrace(); | |
129 } | |
130 } | |
131 } else { | |
132 seqPatch = p1; | |
133 normalizedPatch = p1.normalize(); | |
134 } | |
135 } | |
136 final long end1 = System.currentTimeMillis(); | |
137 // | |
138 // byte[] result = seqPatch.apply(baseRevisionContent, rr.actualLen); | |
139 byte[] result = normalizedPatch.apply(baseRevisionContent, rr.actualLen); | |
140 final long end2 = System.currentTimeMillis(); | |
141 byte[] origin = getRevisionTrueContent(indexFile.getParentFile(), rr.entryIndex, rr.linkRevision); | |
142 final long end3 = System.currentTimeMillis(); | |
143 rr.done(); | |
144 System.out.printf("Collected patches up to revision %d. Patches total: %d, last contains %d elements\n", rr.entryIndex, rr.entryIndex - startEntryIndex + 1, seqPatch.count()); | |
145 if (!Arrays.equals(result, origin)) { | |
146 if (shallDumpDiff) { | |
147 diff(result, origin); | |
148 dumpLineDifference(result, origin); | |
149 } else { | |
150 System.out.println("FAILURE!"); | |
151 } | |
152 } else { | |
153 System.out.println("OK!"); | |
154 System.out.printf("Iterate: %d ms, apply collected: %d ms, total=%d ms; Conventional: %d ms\n", (end1-start), (end2-end1), (end2-start), (end3-end2)); | |
155 } | |
156 Patch normalized = normalizedPatch; //seqPatch.normalize(); | |
157 System.out.printf("N%s\n%d => %d patch elements\n", normalized, seqPatch.count(), normalized.count()); | |
158 // System.out.println(rs); | |
159 } | |
160 | |
161 private void dumpLineDifference(byte[] result, byte[] origin) { | |
162 String rs = new String(result).replace('\0', '\t'); | |
163 String os = new String(origin).replace('\0', '\t'); | |
164 String[] rsLines = rs.split("\n"); | |
165 String[] osLines = os.split("\n"); | |
166 int approxPos = 0; | |
167 for (int i = 0; i < Math.min(rsLines.length, osLines.length); i++) { | |
168 if (!rsLines[i].equals(osLines[i])) { | |
169 System.out.printf("@%d (offset ~%d)\n\t%s\n\t%s\n", i, approxPos, osLines[i], rsLines[i]); | |
170 } | |
171 approxPos += rsLines[i].length() + 1; | |
172 } | |
173 } | |
174 | |
175 private void diff(byte[] result, byte[] origin) { | |
176 DiffHelper<LineSequence> pg = new DiffHelper<LineSequence>(); | |
177 pg.init(LineSequence.newlines(origin), LineSequence.newlines(result)); | |
178 pg.findMatchingBlocks(new DiffHelper.DeltaDumpInspector<LineSequence>()); | |
179 } | |
180 | |
181 private Patch createPatch(RevlogReader rr) throws IOException { | |
182 assert rr.isPatch(); | |
183 if (patchData == null || patchData.capacity() < rr.getDataLength()) { | |
184 patchData = ByteBuffer.allocate(rr.getDataLength()); | |
185 } else { | |
186 patchData.clear(); | |
187 } | |
188 rr.getData(patchData); | |
189 patchData.flip(); | |
190 Patch patch1 = new Patch(); | |
191 patch1.read(new ByteArrayDataAccess(patchData.array(), patchData.arrayOffset(), patchData.remaining())); | |
192 return patch1; | |
193 } | |
194 | |
195 private byte[] getRevisionTrueContent(File repoLoc, final int manifestRev, int clogRev) throws HgRepositoryNotFoundException { | |
196 HgRepository hgRepo = new HgLookup().detect(repoLoc); | |
197 final ByteArrayOutputStream out = new ByteArrayOutputStream(1024 * 1000); | |
198 hgRepo.getManifest().walk(clogRev, clogRev, new HgManifest.Inspector() { | |
199 | |
200 public boolean next(Nodeid nid, Path fname, Flags flags) { | |
201 try { | |
202 out.write(fname.toString().getBytes()); | |
203 out.write(0); | |
204 out.write(nid.toString().getBytes()); | |
205 if (flags == Flags.Exec) { | |
206 out.write('x'); | |
207 } else if (flags == Flags.Link) { | |
208 out.write('l'); | |
209 } | |
210 out.write('\n'); | |
211 } catch (IOException e) { | |
212 throw new IllegalStateException(e); | |
213 } | |
214 return true; | |
215 } | |
216 | |
217 public boolean end(int manifestRevisionIndex) { | |
218 return false; | |
219 } | |
220 | |
221 public boolean begin(int manifestRevisionIndex, Nodeid manifestRevision, int changelogRevisionIndex) { | |
222 if (manifestRev != manifestRevisionIndex) { | |
223 throw new IllegalStateException(String.valueOf(manifestRevisionIndex)); | |
224 } | |
225 return true; | |
226 } | |
227 }); | |
228 return out.toByteArray(); | |
229 } | |
230 } |