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 }