comparison src/org/tmatesoft/hg/repo/Revlog.java @ 324:283b294d1079

Explore alternatives to access file-changelog combined history
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 03 Oct 2011 06:47:20 +0200
parents 09628675bcee
children 3f09b8c19142
comparison
equal deleted inserted replaced
323:4c7e3ba67213 324:283b294d1079
31 import org.tmatesoft.hg.core.HgBadStateException; 31 import org.tmatesoft.hg.core.HgBadStateException;
32 import org.tmatesoft.hg.core.HgException; 32 import org.tmatesoft.hg.core.HgException;
33 import org.tmatesoft.hg.core.Nodeid; 33 import org.tmatesoft.hg.core.Nodeid;
34 import org.tmatesoft.hg.internal.ArrayHelper; 34 import org.tmatesoft.hg.internal.ArrayHelper;
35 import org.tmatesoft.hg.internal.DataAccess; 35 import org.tmatesoft.hg.internal.DataAccess;
36 import org.tmatesoft.hg.internal.Experimental;
36 import org.tmatesoft.hg.internal.RevlogStream; 37 import org.tmatesoft.hg.internal.RevlogStream;
38 import org.tmatesoft.hg.util.Adaptable;
37 import org.tmatesoft.hg.util.ByteChannel; 39 import org.tmatesoft.hg.util.ByteChannel;
38 import org.tmatesoft.hg.util.CancelSupport; 40 import org.tmatesoft.hg.util.CancelSupport;
39 import org.tmatesoft.hg.util.CancelledException; 41 import org.tmatesoft.hg.util.CancelledException;
40 import org.tmatesoft.hg.util.ProgressSupport; 42 import org.tmatesoft.hg.util.ProgressSupport;
41 43
213 System.arraycopy(pc.nodeid, 0, parent2, 0, 20); 215 System.arraycopy(pc.nodeid, 0, parent2, 0, 20);
214 } 216 }
215 } 217 }
216 } 218 }
217 219
220 @Experimental
221 public void walk(int start, int end, final Revlog.Inspector inspector) {
222 int lastRev = getLastRevision();
223 if (start == TIP) {
224 start = lastRev;
225 }
226 if (end == TIP) {
227 end = lastRev;
228 }
229 final RevisionInspector revisionInsp = getAdapter(inspector, RevisionInspector.class);
230 final ParentInspector parentInsp = getAdapter(inspector, ParentInspector.class);
231 final Nodeid[] allRevisions = parentInsp == null ? null : new Nodeid[end - start + 1];
232
233 content.iterate(start, end, false, new RevlogStream.Inspector() {
234
235 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) {
236 Nodeid nid = Nodeid.fromBinary(nodeid, 0);
237 if (revisionInsp != null) {
238 revisionInsp.next(revisionNumber, nid, linkRevision);
239 }
240 if (parentInsp != null) {
241 Nodeid p1 = parent1Revision == -1 ? Nodeid.NULL : allRevisions[parent1Revision];
242 Nodeid p2 = parent2Revision == -1 ? Nodeid.NULL : allRevisions[parent2Revision];
243 allRevisions[revisionNumber] = nid;
244 parentInsp.next(revisionNumber, nid, parent1Revision, parent2Revision, p1, p2);
245 }
246 }
247 });
248 }
249 private static <T> T getAdapter(Object o, Class<T> adapterClass) {
250 if (adapterClass.isInstance(o)) {
251 return adapterClass.cast(o);
252 }
253 if (o instanceof Adaptable) {
254 return ((Adaptable) o).getAdapter(adapterClass);
255 }
256 return null;
257 }
258
259 /**
260 * MARKER
261 */
262 @Experimental
263 public interface Inspector {
264 }
265
266 @Experimental
267 public interface RevisionInspector extends Inspector {
268 void next(int localRevision, Nodeid revision, int linkedRevision);
269 }
270
271 @Experimental
272 public interface ParentInspector extends Inspector {
273 void next(int localRevision, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2);
274 }
275
218 /* 276 /*
219 * XXX think over if it's better to do either: 277 * XXX think over if it's better to do either:
220 * pw = getChangelog().new ParentWalker(); pw.init() and pass pw instance around as needed 278 * pw = getChangelog().new ParentWalker(); pw.init() and pass pw instance around as needed
221 * or 279 * or
222 * add Revlog#getParentWalker(), static class, make cons() and #init package-local, and keep SoftReference to allow walker reuse. 280 * add Revlog#getParentWalker(), static class, make cons() and #init package-local, and keep SoftReference to allow walker reuse.
223 * 281 *
224 * and yes, walker is not a proper name 282 * and yes, walker is not a proper name
225 */ 283 */
226 public final class ParentWalker { 284 public final class ParentWalker implements ParentInspector {
227 285
228 286
229 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering 287 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering
230 private Nodeid[] sorted; // for binary search 288 private Nodeid[] sorted; // for binary search
231 private int[] sorted2natural; 289 private int[] sorted2natural;
239 297
240 public HgRepository getRepo() { 298 public HgRepository getRepo() {
241 return Revlog.this.getRepo(); 299 return Revlog.this.getRepo();
242 } 300 }
243 301
302 public void next(int revisionNumber, Nodeid revision, int parent1Revision, int parent2Revision, Nodeid nidParent1, Nodeid nidParent2) {
303 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) {
304 throw new IllegalStateException(); // sanity, revisions are sequential
305 }
306 int ix = revisionNumber;
307 sequential[ix] = sorted[ix] = revision;
308 if (parent1Revision != -1) {
309 firstParent[ix] = sequential[parent1Revision];
310 }
311 if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1
312 secondParent[ix] = sequential[parent2Revision];
313 }
314 }
315
244 public void init() { 316 public void init() {
245 final RevlogStream stream = Revlog.this.content; 317 final int revisionCount = Revlog.this.getRevisionCount();
246 final int revisionCount = stream.revisionCount();
247 firstParent = new Nodeid[revisionCount]; 318 firstParent = new Nodeid[revisionCount];
248 // although branches/merges are less frequent, and most of secondParent would be -1/null, some sort of 319 // although branches/merges are less frequent, and most of secondParent would be -1/null, some sort of
249 // SparseOrderedList might be handy, provided its inner structures do not overweight simplicity of an array 320 // SparseOrderedList might be handy, provided its inner structures do not overweight simplicity of an array
321 // FIXME IntMap is right candidate?
250 secondParent = new Nodeid[revisionCount]; 322 secondParent = new Nodeid[revisionCount];
251 // 323 //
252 sequential = new Nodeid[revisionCount]; 324 sequential = new Nodeid[revisionCount];
253 sorted = new Nodeid[revisionCount]; 325 sorted = new Nodeid[revisionCount];
254 326 Revlog.this.walk(0, TIP, this);
255 RevlogStream.Inspector insp = new RevlogStream.Inspector() {
256
257 int ix = 0;
258 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) {
259 if (ix != revisionNumber) {
260 // XXX temp code, just to make sure I understand what's going on here
261 // FIXME check against cpython or another tool-mangled repository
262 throw new IllegalStateException();
263 }
264 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) {
265 throw new IllegalStateException(); // sanity, revisions are sequential
266 }
267 final Nodeid nid = new Nodeid(nodeid, true);
268 sequential[ix] = sorted[ix] = nid;
269 if (parent1Revision != -1) {
270 assert parent1Revision < ix;
271 firstParent[ix] = sequential[parent1Revision];
272 }
273 if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1
274 assert parent2Revision < ix;
275 secondParent[ix] = sequential[parent2Revision];
276 }
277 ix++;
278 }
279 };
280 stream.iterate(0, TIP, false, insp);
281 Arrays.sort(sorted); 327 Arrays.sort(sorted);
282 sorted2natural = new int[revisionCount]; 328 sorted2natural = new int[revisionCount];
283 for (int i = 0; i < revisionCount; i++) { 329 for (int i = 0; i < revisionCount; i++) {
284 Nodeid n = sequential[i]; 330 Nodeid n = sequential[i];
285 int x = Arrays.binarySearch(sorted, n); 331 int x = Arrays.binarySearch(sorted, n);
422 * multiple {@link Revlog#getLocalRevision(Nodeid)} calls. 468 * multiple {@link Revlog#getLocalRevision(Nodeid)} calls.
423 * 469 *
424 * getLocalRevision(Nodeid) with straightforward lookup approach performs O(n/2) 470 * getLocalRevision(Nodeid) with straightforward lookup approach performs O(n/2)
425 * #localRevision() is log(n), plus initialization is O(n) 471 * #localRevision() is log(n), plus initialization is O(n)
426 */ 472 */
427 public final class RevisionMap { 473 public final class RevisionMap implements RevisionInspector {
428 /* 474 /*
429 * in fact, initialization is much slower as it instantiates Nodeids, while #getLocalRevision 475 * in fact, initialization is much slower as it instantiates Nodeids, while #getLocalRevision
430 * compares directly against byte buffer. Measuring cpython with 70k+ gives 3 times difference (47 vs 171) 476 * compares directly against byte buffer. Measuring cpython with 70k+ gives 3 times difference (47 vs 171)
431 * for complete changelog iteration. 477 * for complete changelog iteration.
432 */ 478 */
433 479
434 /* 480 /*
435 * XXX 3 * (x * 4) bytes. Can I do better? 481 * XXX 3 * (x * 4) bytes. Can I do better?
482 * It seems, yes. Don't need to keep sorted, always can emulate it with indirect access to sequential through sorted2natural.
483 * i.e. instead sorted[mid].compareTo(toFind), do sequential[sorted2natural[mid]].compareTo(toFind)
436 */ 484 */
437 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering 485 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering
438 private Nodeid[] sorted; // for binary search 486 private Nodeid[] sorted; // for binary search
439 private int[] sorted2natural; 487 private int[] sorted2natural;
440 488
441 public RevisionMap() { 489 public RevisionMap() {
442 } 490 }
443 491
444 public HgRepository getRepo() { 492 public HgRepository getRepo() {
445 return Revlog.this.getRepo(); 493 return Revlog.this.getRepo();
494 }
495
496 public void next(int localRevision, Nodeid revision, int linkedRevision) {
497 sequential[localRevision] = sorted[localRevision] = revision;
446 } 498 }
447 499
448 /** 500 /**
449 * @return <code>this</code> for convenience. 501 * @return <code>this</code> for convenience.
450 */ 502 */
451 public RevisionMap init(/*XXX Pool<Nodeid> to reuse nodeids, if possible. */) { 503 public RevisionMap init(/*XXX Pool<Nodeid> to reuse nodeids, if possible. */) {
452 // XXX HgRepository.register((RepoChangeListener) this); // listen to changes in repo, re-init if needed? 504 // XXX HgRepository.register((RepoChangeListener) this); // listen to changes in repo, re-init if needed?
453 final int revisionCount = Revlog.this.content.revisionCount(); 505 final int revisionCount = Revlog.this.getRevisionCount();
454 sequential = new Nodeid[revisionCount]; 506 sequential = new Nodeid[revisionCount];
455 sorted = new Nodeid[revisionCount]; 507 sorted = new Nodeid[revisionCount];
456 RevlogStream.Inspector insp = new RevlogStream.Inspector() { 508 Revlog.this.walk(0, TIP, this);
457
458 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) {
459 final Nodeid nid = new Nodeid(nodeid, true);
460 sequential[revisionNumber] = sorted[revisionNumber] = nid;
461 }
462 };
463 Revlog.this.content.iterate(0, TIP, false, insp);
464 // next is alternative to Arrays.sort(sorted), and build sorted2natural looking up each element of sequential in sorted. 509 // next is alternative to Arrays.sort(sorted), and build sorted2natural looking up each element of sequential in sorted.
465 // the way sorted2natural was build is O(n*log n). 510 // the way sorted2natural was build is O(n*log n).
466 final ArrayHelper ah = new ArrayHelper(); 511 final ArrayHelper ah = new ArrayHelper();
467 ah.sort(sorted); 512 ah.sort(sorted);
468 // note, values in ArrayHelper#getReversed are 1-based indexes, not 0-based 513 // note, values in ArrayHelper#getReversed are 1-based indexes, not 0-based