Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgManifest.java @ 600:46f29b73e51e
Utilize RevisionLookup to speed-up getRevisionIndex of both manifest and changelog
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 03 May 2013 17:03:31 +0200 |
| parents | d29d9dc6c128 |
| children | 8143c1f77d45 |
comparison
equal
deleted
inserted
replaced
| 599:55b7987c1796 | 600:46f29b73e51e |
|---|---|
| 33 import org.tmatesoft.hg.internal.IdentityPool; | 33 import org.tmatesoft.hg.internal.IdentityPool; |
| 34 import org.tmatesoft.hg.internal.IntMap; | 34 import org.tmatesoft.hg.internal.IntMap; |
| 35 import org.tmatesoft.hg.internal.IntVector; | 35 import org.tmatesoft.hg.internal.IntVector; |
| 36 import org.tmatesoft.hg.internal.IterateControlMediator; | 36 import org.tmatesoft.hg.internal.IterateControlMediator; |
| 37 import org.tmatesoft.hg.internal.Lifecycle; | 37 import org.tmatesoft.hg.internal.Lifecycle; |
| 38 import org.tmatesoft.hg.internal.RevisionLookup; | |
| 38 import org.tmatesoft.hg.internal.RevlogStream; | 39 import org.tmatesoft.hg.internal.RevlogStream; |
| 39 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; | 40 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; |
| 40 import org.tmatesoft.hg.util.CancelSupport; | 41 import org.tmatesoft.hg.util.CancelSupport; |
| 41 import org.tmatesoft.hg.util.LogFacility.Severity; | 42 import org.tmatesoft.hg.util.LogFacility.Severity; |
| 42 import org.tmatesoft.hg.util.Path; | 43 import org.tmatesoft.hg.util.Path; |
| 121 return 0644; | 122 return 0644; |
| 122 } | 123 } |
| 123 } | 124 } |
| 124 | 125 |
| 125 /*package-local*/ HgManifest(HgRepository hgRepo, RevlogStream content, EncodingHelper eh) { | 126 /*package-local*/ HgManifest(HgRepository hgRepo, RevlogStream content, EncodingHelper eh) { |
| 126 super(hgRepo, content); | 127 super(hgRepo, content, true); |
| 127 encodingHelper = eh; | 128 encodingHelper = eh; |
| 128 pathFactory = hgRepo.getSessionContext().getPathFactory(); | 129 pathFactory = hgRepo.getSessionContext().getPathFactory(); |
| 129 } | 130 } |
| 130 | 131 |
| 131 /** | 132 /** |
| 242 if (changesetRevisionIndex == HgRepository.WORKING_COPY || changesetRevisionIndex == HgRepository.BAD_REVISION) { | 243 if (changesetRevisionIndex == HgRepository.WORKING_COPY || changesetRevisionIndex == HgRepository.BAD_REVISION) { |
| 243 throw new HgInvalidRevisionException("Can't use constants like WORKING_COPY or BAD_REVISION", null, changesetRevisionIndex); | 244 throw new HgInvalidRevisionException("Can't use constants like WORKING_COPY or BAD_REVISION", null, changesetRevisionIndex); |
| 244 } | 245 } |
| 245 // revisionNumber == TIP is processed by RevisionMapper | 246 // revisionNumber == TIP is processed by RevisionMapper |
| 246 if (revisionMap == null) { | 247 if (revisionMap == null) { |
| 247 revisionMap = new RevisionMapper(getRepo()); | 248 revisionMap = new RevisionMapper(super.revisionLookup == null); |
| 248 content.iterate(0, TIP, false, revisionMap); | 249 content.iterate(0, TIP, false, revisionMap); |
| 250 revisionMap.fixReusedManifests(); | |
| 251 if (super.useRevisionLookup && super.revisionLookup == null) { | |
| 252 // reuse RevisionLookup if there's none yet | |
| 253 super.revisionLookup = revisionMap.manifestNodeids; | |
| 254 } | |
| 255 revisionMap.manifestNodeids = null; | |
| 249 } | 256 } |
| 250 return revisionMap.at(changesetRevisionIndex); | 257 return revisionMap.at(changesetRevisionIndex); |
| 251 } | 258 } |
| 252 | 259 |
| 253 /** | 260 /** |
| 570 public void finish(Object token) { | 577 public void finish(Object token) { |
| 571 progressHelper.done(); | 578 progressHelper.done(); |
| 572 } | 579 } |
| 573 } | 580 } |
| 574 | 581 |
| 575 private static class RevisionMapper implements RevlogStream.Inspector, Lifecycle { | 582 private class RevisionMapper implements RevlogStream.Inspector, Lifecycle { |
| 576 | 583 |
| 577 private final int changelogRevisionCount; | 584 private final int changelogRevisionCount; |
| 578 private int[] changelog2manifest; | 585 private int[] changelog2manifest; |
| 579 private final HgRepository repo; | 586 RevisionLookup manifestNodeids; |
| 580 private int[] manifestNodeidHashes; // first 4 bytes of manifest nodeid at corresponding index | 587 |
| 581 | 588 private RevisionMapper(boolean useOwnRevisionLookup) { |
| 582 public RevisionMapper(HgRepository hgRepo) { | 589 changelogRevisionCount = HgManifest.this.getRepo().getChangelog().getRevisionCount(); |
| 583 repo = hgRepo; | 590 if (useOwnRevisionLookup) { |
| 584 changelogRevisionCount = repo.getChangelog().getRevisionCount(); | 591 manifestNodeids = new RevisionLookup(HgManifest.this.content); |
| 585 } | 592 } |
| 586 | 593 } |
| 594 | |
| 587 /** | 595 /** |
| 588 * Get index of manifest revision that corresponds to specified changeset | 596 * Get index of manifest revision that corresponds to specified changeset |
| 589 * @param changesetRevisionIndex non-negative index of changelog revision, or {@link HgRepository#TIP} | 597 * @param changesetRevisionIndex non-negative index of changelog revision, or {@link HgRepository#TIP} |
| 590 * @return index of manifest revision, or {@link HgRepository#BAD_REVISION} if changeset doesn't reference a valid manifest | 598 * @return index of manifest revision, or {@link HgRepository#BAD_REVISION} if changeset doesn't reference a valid manifest |
| 591 * @throws HgInvalidRevisionException if method argument specifies non-existent revision index | 599 * @throws HgInvalidRevisionException if method argument specifies non-existent revision index |
| 601 return changelog2manifest[changesetRevisionIndex]; | 609 return changelog2manifest[changesetRevisionIndex]; |
| 602 } | 610 } |
| 603 return changesetRevisionIndex; | 611 return changesetRevisionIndex; |
| 604 } | 612 } |
| 605 | 613 |
| 606 // XXX likely can be replaced with Revlog.RevisionInspector | 614 // XXX can be replaced with Revlog.RevisionInspector, but I don't want Nodeid instances |
| 607 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | 615 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { |
| 608 if (changelog2manifest != null) { | 616 if (changelog2manifest != null) { |
| 609 // next assertion is not an error, rather assumption check, which is too development-related to be explicit exception - | 617 // next assertion is not an error, rather assumption check, which is too development-related to be explicit exception - |
| 610 // I just wonder if there are manifests that have two entries pointing to single changeset. It seems unrealistic, though - | 618 // I just wonder if there are manifests that have two entries pointing to single changeset. It seems unrealistic, though - |
| 611 // changeset records one and only one manifest nodeid | 619 // changeset records one and only one manifest nodeid |
| 618 for (int i = 0; i < revisionNumber; changelog2manifest[i] = i, i++) | 626 for (int i = 0; i < revisionNumber; changelog2manifest[i] = i, i++) |
| 619 ; | 627 ; |
| 620 changelog2manifest[linkRevision] = revisionNumber; | 628 changelog2manifest[linkRevision] = revisionNumber; |
| 621 } | 629 } |
| 622 } | 630 } |
| 623 manifestNodeidHashes[revisionNumber] = Nodeid.hashCode(nodeid); | 631 if (manifestNodeids != null) { |
| 632 manifestNodeids.next(revisionNumber, nodeid); | |
| 633 } | |
| 624 } | 634 } |
| 625 | 635 |
| 626 public void start(int count, Callback callback, Object token) { | 636 public void start(int count, Callback callback, Object token) { |
| 627 if (count != changelogRevisionCount) { | 637 if (count != changelogRevisionCount) { |
| 628 assert count < changelogRevisionCount; // no idea what to do if manifest has more revisions than changelog | 638 assert count < changelogRevisionCount; // no idea what to do if manifest has more revisions than changelog |
| 631 // Note, it's pure guess, I didn't see such repository yet (although the way manifest revisions | 641 // Note, it's pure guess, I didn't see such repository yet (although the way manifest revisions |
| 632 // in cpython repo are numbered makes me think aforementioned way) | 642 // in cpython repo are numbered makes me think aforementioned way) |
| 633 changelog2manifest = new int[changelogRevisionCount]; | 643 changelog2manifest = new int[changelogRevisionCount]; |
| 634 Arrays.fill(changelog2manifest, BAD_REVISION); | 644 Arrays.fill(changelog2manifest, BAD_REVISION); |
| 635 } | 645 } |
| 636 manifestNodeidHashes = new int[count]; | 646 if (manifestNodeids != null) { |
| 647 manifestNodeids.prepare(count); | |
| 648 } | |
| 637 } | 649 } |
| 638 | 650 |
| 639 public void finish(Object token) { | 651 public void finish(Object token) { |
| 652 // it's not a nice idea to fix changesets that reuse existing manifest entries from inside | |
| 653 // #finish, as the manifest read operation is not complete at the moment. | |
| 654 } | |
| 655 | |
| 656 public void fixReusedManifests() { | |
| 640 if (changelog2manifest == null) { | 657 if (changelog2manifest == null) { |
| 658 // direct, 1-1 mapping of changeset indexes to manifest | |
| 641 return; | 659 return; |
| 642 } | 660 } |
| 643 // I assume there'd be not too many revisions we don't know manifest of | 661 // I assume there'd be not too many revisions we don't know manifest of |
| 644 IntVector undefinedChangelogRevision = new IntVector(); | 662 IntVector undefinedChangelogRevision = new IntVector(); |
| 645 for (int i = 0; i < changelog2manifest.length; i++) { | 663 for (int i = 0; i < changelog2manifest.length; i++) { |
| 650 if (undefinedChangelogRevision.size() > 0) { | 668 if (undefinedChangelogRevision.size() > 0) { |
| 651 final long t1 = System.nanoTime(); | 669 final long t1 = System.nanoTime(); |
| 652 final IntMap<Nodeid> missingCsetToManifest = new IntMap<Nodeid>(undefinedChangelogRevision.size()); | 670 final IntMap<Nodeid> missingCsetToManifest = new IntMap<Nodeid>(undefinedChangelogRevision.size()); |
| 653 int[] undefinedClogRevs = undefinedChangelogRevision.toArray(); | 671 int[] undefinedClogRevs = undefinedChangelogRevision.toArray(); |
| 654 // undefinedChangelogRevision is sorted by the nature it's created | 672 // undefinedChangelogRevision is sorted by the nature it's created |
| 655 repo.getChangelog().rangeInternal(new HgChangelog.Inspector() { | 673 HgManifest.this.getRepo().getChangelog().rangeInternal(new HgChangelog.Inspector() { |
| 656 | 674 |
| 657 public void next(int revisionIndex, Nodeid nodeid, RawChangeset cset) { | 675 public void next(int revisionIndex, Nodeid nodeid, RawChangeset cset) { |
| 658 missingCsetToManifest.put(revisionIndex, cset.manifest()); | 676 missingCsetToManifest.put(revisionIndex, cset.manifest()); |
| 659 } | 677 } |
| 660 }, undefinedClogRevs); | 678 }, undefinedClogRevs); |
| 661 assert missingCsetToManifest.size() == undefinedChangelogRevision.size(); | 679 assert missingCsetToManifest.size() == undefinedChangelogRevision.size(); |
| 662 final long t2 = System.nanoTime(); | 680 final long t2 = System.nanoTime(); |
| 663 for (int u : undefinedClogRevs) { | 681 for (int u : undefinedClogRevs) { |
| 664 Nodeid manifest = missingCsetToManifest.get(u); | 682 Nodeid manifest = missingCsetToManifest.get(u); |
| 665 if (manifest == null || manifest.isNull()) { | 683 if (manifest == null || manifest.isNull()) { |
| 666 repo.getSessionContext().getLog().dump(getClass(), Severity.Warn, "Changeset %d has no associated manifest entry", u); | 684 HgManifest.this.getRepo().getSessionContext().getLog().dump(getClass(), Severity.Warn, "Changeset %d has no associated manifest entry", u); |
| 667 // keep -1 in the changelog2manifest map. | 685 // keep BAD_REVISION in the changelog2manifest map. |
| 668 continue; | 686 continue; |
| 669 } | 687 } |
| 670 int hash = manifest.hashCode(); | 688 if (manifestNodeids != null) { |
| 671 for (int i = 0; i < manifestNodeidHashes.length; i++) { | 689 int manifestRevIndex = manifestNodeids.findIndex(manifest); |
| 672 if (manifestNodeidHashes[i] == hash) { | 690 // mimic HgManifest#getRevisionIndex() to keep behavior the same |
| 673 if (manifest.equals(repo.getManifest().getRevision(i))) { | 691 if (manifestRevIndex == BAD_REVISION) { |
| 674 changelog2manifest[u] = i; | 692 throw new HgInvalidRevisionException(String.format("Can't find index of revision %s", manifest.shortNotation()), manifest, null); |
| 675 break; | |
| 676 } | |
| 677 // else false match (only 4 head bytes matched, continue loop | |
| 678 } | 693 } |
| 694 changelog2manifest[u] = manifestRevIndex; | |
| 695 } else { | |
| 696 changelog2manifest[u] = HgManifest.this.getRevisionIndex(manifest); | |
| 679 } | 697 } |
| 680 } | 698 } |
| 681 final long t3 = System.nanoTime(); | 699 final long t3 = System.nanoTime(); |
| 682 System.out.printf("\tRevisionMapper#finish(), %d missing revs: %d + %d us\n", undefinedChangelogRevision.size(), (t2-t1) / 1000, (t3-t2) / 1000); | 700 System.out.printf("\tRevisionMapper#finish(), %d missing revs: %d + %d us\n", undefinedChangelogRevision.size(), (t2-t1) / 1000, (t3-t2) / 1000); |
| 683 } | 701 } |
| 684 manifestNodeidHashes = null; | |
| 685 } | 702 } |
| 686 } | 703 } |
| 687 | 704 |
| 688 /** | 705 /** |
| 689 * Look up specified file in possibly multiple manifest revisions, collect file revision and flags. | 706 * Look up specified file in possibly multiple manifest revisions, collect file revision and flags. |
