Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/Revlog.java @ 243:0e01f9182e16
External cache Nodeid<->int added, Revlog.RevisionMap
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Thu, 23 Jun 2011 16:58:38 +0200 |
| parents | 047b1dec7a04 |
| children | 9fb50c04f03c |
comparison
equal
deleted
inserted
replaced
| 242:ad6a046943be | 243:0e01f9182e16 |
|---|---|
| 14 * the terms of a license other than GNU General Public License | 14 * the terms of a license other than GNU General Public License |
| 15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
| 16 */ | 16 */ |
| 17 package org.tmatesoft.hg.repo; | 17 package org.tmatesoft.hg.repo; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.core.Nodeid.NULL; | |
| 19 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; | 20 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; |
| 20 import static org.tmatesoft.hg.repo.HgRepository.TIP; | 21 import static org.tmatesoft.hg.repo.HgRepository.TIP; |
| 21 | 22 |
| 22 import java.io.IOException; | 23 import java.io.IOException; |
| 23 import java.nio.ByteBuffer; | 24 import java.nio.ByteBuffer; |
| 85 public final Nodeid getRevision(int revision) { | 86 public final Nodeid getRevision(int revision) { |
| 86 // XXX cache nodeids? | 87 // XXX cache nodeids? |
| 87 return Nodeid.fromBinary(content.nodeid(revision), 0); | 88 return Nodeid.fromBinary(content.nodeid(revision), 0); |
| 88 } | 89 } |
| 89 | 90 |
| 91 /** | |
| 92 * Get local revision number (index) of the specified revision. | |
| 93 * | |
| 94 * For occasional queries, this method works with decent performance, despite its O(n/2) approach. | |
| 95 * Alternatively, if you need to perform multiple queries (e.g. at least 15-20), {@link RevisionMap} may come handy. | |
| 96 * | |
| 97 * @param nid revision to look up | |
| 98 * @return revision index, or {@link HgRepository#BAD_REVISION} if specified revision not found. | |
| 99 */ | |
| 90 public final int getLocalRevision(Nodeid nid) { | 100 public final int getLocalRevision(Nodeid nid) { |
| 91 int revision = content.findLocalRevisionNumber(nid); | 101 int revision = content.findLocalRevisionNumber(nid); |
| 92 if (revision == BAD_REVISION) { | 102 if (revision == BAD_REVISION) { |
| 93 throw new IllegalArgumentException(String.format("%s doesn't represent a revision of %s", nid.toString(), this /*XXX HgDataFile.getPath might be more suitable here*/)); | 103 throw new IllegalArgumentException(String.format("%s doesn't represent a revision of %s", nid.toString(), this /*XXX HgDataFile.getPath might be more suitable here*/)); |
| 94 } | 104 } |
| 227 | 237 |
| 228 int ix = 0; | 238 int ix = 0; |
| 229 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { | 239 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { |
| 230 if (ix != revisionNumber) { | 240 if (ix != revisionNumber) { |
| 231 // XXX temp code, just to make sure I understand what's going on here | 241 // XXX temp code, just to make sure I understand what's going on here |
| 242 // FIXME check against cpython or another tool-mangled repository | |
| 232 throw new IllegalStateException(); | 243 throw new IllegalStateException(); |
| 233 } | 244 } |
| 234 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { | 245 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { |
| 235 throw new IllegalStateException(); // sanity, revisions are sequential | 246 throw new IllegalStateException(); // sanity, revisions are sequential |
| 236 } | 247 } |
| 368 } | 379 } |
| 369 return false; | 380 return false; |
| 370 } | 381 } |
| 371 } | 382 } |
| 372 | 383 |
| 384 /** | |
| 385 * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of | |
| 386 * multiple {@link Revlog#getLocalRevision(Nodeid)} calls. | |
| 387 * | |
| 388 * getLocalRevision(Nodeid) with straightforward lookup approach performs O(n/2) | |
| 389 * #localRevision() is log(n), plus initialization is O(n) | |
| 390 */ | |
| 391 public final class RevisionMap { | |
| 392 /* | |
| 393 * in fact, initialization is much slower as it instantiates Nodeids, while #getLocalRevision | |
| 394 * compares directly against byte buffer. Measuring cpython with 70k+ gives 3 times difference (47 vs 171) | |
| 395 * for complete changelog iteration. | |
| 396 */ | |
| 397 | |
| 398 /* | |
| 399 * XXX 3 * (x * 4) bytes. Can I do better? | |
| 400 */ | |
| 401 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering | |
| 402 private Nodeid[] sorted; // for binary search | |
| 403 private int[] sorted2natural; | |
| 404 | |
| 405 public RevisionMap() { | |
| 406 } | |
| 407 | |
| 408 public HgRepository getRepo() { | |
| 409 return Revlog.this.getRepo(); | |
| 410 } | |
| 411 | |
| 412 /** | |
| 413 * @return <code>this</code> for convenience. | |
| 414 */ | |
| 415 public RevisionMap init(/*XXX Pool<Nodeid> to reuse nodeids, if possible. */) { | |
| 416 // XXX HgRepository.register((RepoChangeListener) this); // listen to changes in repo, re-init if needed? | |
| 417 final int revisionCount = Revlog.this.content.revisionCount(); | |
| 418 sequential = new Nodeid[revisionCount]; | |
| 419 sorted = new Nodeid[revisionCount]; | |
| 420 sorted2natural = new int[revisionCount]; | |
| 421 RevlogStream.Inspector insp = new RevlogStream.Inspector() { | |
| 422 | |
| 423 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | |
| 424 final Nodeid nid = new Nodeid(nodeid, true); | |
| 425 sequential[revisionNumber] = sorted[revisionNumber] = nid; | |
| 426 } | |
| 427 }; | |
| 428 Revlog.this.content.iterate(0, TIP, false, insp); | |
| 429 Arrays.sort(sorted); | |
| 430 for (int i = 0; i < revisionCount; i++) { | |
| 431 Nodeid n = sequential[i]; | |
| 432 int x = Arrays.binarySearch(sorted, n); | |
| 433 sorted2natural[x] = i; | |
| 434 } | |
| 435 return this; | |
| 436 } | |
| 437 | |
| 438 public Nodeid revision(int localRevision) { | |
| 439 return sequential[localRevision]; | |
| 440 } | |
| 441 public int localRevision(Nodeid revision) { | |
| 442 if (revision == null || NULL.equals(revision)) { | |
| 443 return BAD_REVISION; | |
| 444 } | |
| 445 int x = Arrays.binarySearch(sorted, revision); | |
| 446 if (x < 0) { | |
| 447 return BAD_REVISION; | |
| 448 } | |
| 449 return sorted2natural[x]; | |
| 450 } | |
| 451 } | |
| 452 | |
| 453 | |
| 373 protected static class ContentPipe implements RevlogStream.Inspector, CancelSupport { | 454 protected static class ContentPipe implements RevlogStream.Inspector, CancelSupport { |
| 374 private final ByteChannel sink; | 455 private final ByteChannel sink; |
| 375 private final CancelSupport cancelSupport; | 456 private final CancelSupport cancelSupport; |
| 376 private Exception failure; | 457 private Exception failure; |
| 377 private final int offset; | 458 private final int offset; |
