Mercurial > hg4j
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. |