Mercurial > hg4j
diff 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 |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/Revlog.java Fri Sep 30 08:44:48 2011 +0200 +++ b/src/org/tmatesoft/hg/repo/Revlog.java Mon Oct 03 06:47:20 2011 +0200 @@ -33,7 +33,9 @@ import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ArrayHelper; import org.tmatesoft.hg.internal.DataAccess; +import org.tmatesoft.hg.internal.Experimental; import org.tmatesoft.hg.internal.RevlogStream; +import org.tmatesoft.hg.util.Adaptable; import org.tmatesoft.hg.util.ByteChannel; import org.tmatesoft.hg.util.CancelSupport; import org.tmatesoft.hg.util.CancelledException; @@ -215,6 +217,62 @@ } } + @Experimental + public void walk(int start, int end, final Revlog.Inspector inspector) { + int lastRev = getLastRevision(); + if (start == TIP) { + start = lastRev; + } + if (end == TIP) { + end = lastRev; + } + final RevisionInspector revisionInsp = getAdapter(inspector, RevisionInspector.class); + final ParentInspector parentInsp = getAdapter(inspector, ParentInspector.class); + final Nodeid[] allRevisions = parentInsp == null ? null : new Nodeid[end - start + 1]; + + content.iterate(start, end, false, new RevlogStream.Inspector() { + + public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { + Nodeid nid = Nodeid.fromBinary(nodeid, 0); + if (revisionInsp != null) { + revisionInsp.next(revisionNumber, nid, linkRevision); + } + if (parentInsp != null) { + Nodeid p1 = parent1Revision == -1 ? Nodeid.NULL : allRevisions[parent1Revision]; + Nodeid p2 = parent2Revision == -1 ? Nodeid.NULL : allRevisions[parent2Revision]; + allRevisions[revisionNumber] = nid; + parentInsp.next(revisionNumber, nid, parent1Revision, parent2Revision, p1, p2); + } + } + }); + } + private static <T> T getAdapter(Object o, Class<T> adapterClass) { + if (adapterClass.isInstance(o)) { + return adapterClass.cast(o); + } + if (o instanceof Adaptable) { + return ((Adaptable) o).getAdapter(adapterClass); + } + return null; + } + + /** + * MARKER + */ + @Experimental + public interface Inspector { + } + + @Experimental + public interface RevisionInspector extends Inspector { + void next(int localRevision, Nodeid revision, int linkedRevision); + } + + @Experimental + public interface ParentInspector extends Inspector { + void next(int localRevision, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2); + } + /* * XXX think over if it's better to do either: * pw = getChangelog().new ParentWalker(); pw.init() and pass pw instance around as needed @@ -223,7 +281,7 @@ * * and yes, walker is not a proper name */ - public final class ParentWalker { + public final class ParentWalker implements ParentInspector { private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering @@ -241,43 +299,31 @@ return Revlog.this.getRepo(); } + public void next(int revisionNumber, Nodeid revision, int parent1Revision, int parent2Revision, Nodeid nidParent1, Nodeid nidParent2) { + if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { + throw new IllegalStateException(); // sanity, revisions are sequential + } + int ix = revisionNumber; + sequential[ix] = sorted[ix] = revision; + if (parent1Revision != -1) { + firstParent[ix] = sequential[parent1Revision]; + } + if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1 + secondParent[ix] = sequential[parent2Revision]; + } + } + public void init() { - final RevlogStream stream = Revlog.this.content; - final int revisionCount = stream.revisionCount(); + final int revisionCount = Revlog.this.getRevisionCount(); firstParent = new Nodeid[revisionCount]; // although branches/merges are less frequent, and most of secondParent would be -1/null, some sort of // SparseOrderedList might be handy, provided its inner structures do not overweight simplicity of an array + // FIXME IntMap is right candidate? secondParent = new Nodeid[revisionCount]; // sequential = new Nodeid[revisionCount]; sorted = new Nodeid[revisionCount]; - - RevlogStream.Inspector insp = new RevlogStream.Inspector() { - - int ix = 0; - public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { - if (ix != revisionNumber) { - // XXX temp code, just to make sure I understand what's going on here - // FIXME check against cpython or another tool-mangled repository - throw new IllegalStateException(); - } - if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { - throw new IllegalStateException(); // sanity, revisions are sequential - } - final Nodeid nid = new Nodeid(nodeid, true); - sequential[ix] = sorted[ix] = nid; - if (parent1Revision != -1) { - assert parent1Revision < ix; - firstParent[ix] = sequential[parent1Revision]; - } - if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1 - assert parent2Revision < ix; - secondParent[ix] = sequential[parent2Revision]; - } - ix++; - } - }; - stream.iterate(0, TIP, false, insp); + Revlog.this.walk(0, TIP, this); Arrays.sort(sorted); sorted2natural = new int[revisionCount]; for (int i = 0; i < revisionCount; i++) { @@ -424,7 +470,7 @@ * getLocalRevision(Nodeid) with straightforward lookup approach performs O(n/2) * #localRevision() is log(n), plus initialization is O(n) */ - public final class RevisionMap { + public final class RevisionMap implements RevisionInspector { /* * in fact, initialization is much slower as it instantiates Nodeids, while #getLocalRevision * compares directly against byte buffer. Measuring cpython with 70k+ gives 3 times difference (47 vs 171) @@ -432,7 +478,9 @@ */ /* - * XXX 3 * (x * 4) bytes. Can I do better? + * XXX 3 * (x * 4) bytes. Can I do better? + * It seems, yes. Don't need to keep sorted, always can emulate it with indirect access to sequential through sorted2natural. + * i.e. instead sorted[mid].compareTo(toFind), do sequential[sorted2natural[mid]].compareTo(toFind) */ private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering private Nodeid[] sorted; // for binary search @@ -444,23 +492,20 @@ public HgRepository getRepo() { return Revlog.this.getRepo(); } + + public void next(int localRevision, Nodeid revision, int linkedRevision) { + sequential[localRevision] = sorted[localRevision] = revision; + } /** * @return <code>this</code> for convenience. */ public RevisionMap init(/*XXX Pool<Nodeid> to reuse nodeids, if possible. */) { // XXX HgRepository.register((RepoChangeListener) this); // listen to changes in repo, re-init if needed? - final int revisionCount = Revlog.this.content.revisionCount(); + final int revisionCount = Revlog.this.getRevisionCount(); sequential = new Nodeid[revisionCount]; sorted = new Nodeid[revisionCount]; - RevlogStream.Inspector insp = new RevlogStream.Inspector() { - - public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { - final Nodeid nid = new Nodeid(nodeid, true); - sequential[revisionNumber] = sorted[revisionNumber] = nid; - } - }; - Revlog.this.content.iterate(0, TIP, false, insp); + Revlog.this.walk(0, TIP, this); // next is alternative to Arrays.sort(sorted), and build sorted2natural looking up each element of sequential in sorted. // the way sorted2natural was build is O(n*log n). final ArrayHelper ah = new ArrayHelper();