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.