Mercurial > hg4j
changeset 695:053bb4397bf9
Refactoring: nice Revlog.indexWalk() implementation
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Mon, 05 Aug 2013 18:45:16 +0200 |
parents | 7efabe0cddcf |
children | 5b5d199e2eb3 |
files | src/org/tmatesoft/hg/internal/RevlogDelegate.java src/org/tmatesoft/hg/repo/Revlog.java |
diffstat | 2 files changed, 224 insertions(+), 84 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/RevlogDelegate.java Mon Aug 05 18:45:16 2013 +0200 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.internal; + +import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.internal.RevlogStream.Inspector; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgRuntimeException; + +/** + * Does almost nothing, facilitates chains of inspectors and sharing certain information between them + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public abstract class RevlogDelegate implements RevlogStream.Inspector { + + private final Inspector next; + private Nodeid nid; + private final RevlogDelegate nextAsRD; + + protected RevlogDelegate(RevlogStream.Inspector nextInChain) { + next = nextInChain; + if (nextInChain instanceof RevlogDelegate) { + // additional benefits + nextAsRD = (RevlogDelegate) nextInChain; + } else { + nextAsRD = null; + } + } + + /** + * iterates index only and ensures pre/post processing is invoked for the chain + */ + public void walk(HgRepository hgRepo, RevlogStream stream, int from, int to) { + // hgRepo is handy for present uses, but is generally not appropriate, + // it's ok to refactor and let subclasses get what they need through e.g. a cons + stream.iterate(from, to, false, this); + postWalk(hgRepo); + } + + // does nothing but gives a chance to delegate to handle the same + protected void postWalk(HgRepository hgRepo) { + if (nextAsRD != null) { + nextAsRD.postWalk(hgRepo); + } + } + + /** + * @return Nodeid of current revision if already known, or a new instance otherwise. + * The value will propagate to subsequent {@link RevlogDelegate RevlogDelegates}, if any + */ + protected Nodeid getRevision(byte[] nodeid) { + if (nid == null) { + nid = Nodeid.fromBinary(nodeid, 0); + } + return nid; + } + + protected void setRevision(Nodeid nodeid) { + nid = nodeid; + } + + public void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgRuntimeException { + if (next != null) { + if (nextAsRD != null) { + nextAsRD.setRevision(nid); // null is fine + } + next.next(revisionIndex, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeid, data); + } + nid = null; // forget it, get ready for the next iteration + } +}
--- a/src/org/tmatesoft/hg/repo/Revlog.java Mon Aug 05 17:42:10 2013 +0200 +++ b/src/org/tmatesoft/hg/repo/Revlog.java Mon Aug 05 18:45:16 2013 +0200 @@ -31,6 +31,7 @@ import org.tmatesoft.hg.internal.IntMap; import org.tmatesoft.hg.internal.Preview; import org.tmatesoft.hg.internal.RevisionLookup; +import org.tmatesoft.hg.internal.RevlogDelegate; import org.tmatesoft.hg.internal.RevlogStream; import org.tmatesoft.hg.util.Adaptable; import org.tmatesoft.hg.util.ByteChannel; @@ -352,92 +353,21 @@ final RevisionInspector revisionInsp = Adaptable.Factory.getAdapter(inspector, RevisionInspector.class, null); final ParentInspector parentInsp = Adaptable.Factory.getAdapter(inspector, ParentInspector.class, null); final LinkRevisionInspector linkRevInspector = Adaptable.Factory.getAdapter(inspector, LinkRevisionInspector.class, null); - final Nodeid[] allRevisions = parentInsp == null ? null : new Nodeid[end - _start + 1]; - // next are to build set of parent indexes that are not part of the range iteration - // i.e. those parents we need to read separately. See Issue 31 for details. - final int[] firstParentIndexes = parentInsp == null || _start == 0 ? null : new int[allRevisions.length]; - final int[] secondParentIndexes = parentInsp == null || _start == 0 ? null : new int[allRevisions.length]; - final IntMap<Nodeid> missingParents = parentInsp == null || _start == 0 ? null : new IntMap<Nodeid>(16); + + // instantiate implementors in reverse order + RevlogDelegate head = parentInsp == null ? null : new ParentDelegate(parentInsp, null, _start, end); + if (revisionInsp != null) { + head = new RevisionDelegate(revisionInsp, head); + } + if (linkRevInspector != null) { + head = new LinkRevDelegate(linkRevInspector, head); + } + // first to get notified is created last - content.iterate(_start, end, false, new RevlogStream.Inspector() { - private int i = 0; - - public void next(int revisionIndex, int actualLen, int baseRevIndex, int linkRevIndex, int parent1RevIndex, int parent2RevIndex, byte[] nodeid, DataAccess data) throws HgRuntimeException { - // FIXME refactor either as chain of RevlogStream.Inspector or an external AnyOf() runner not to keep everything - // in a single method - if (linkRevInspector != null) { - linkRevInspector.next(revisionIndex, linkRevIndex); - if (parentInsp == null && revisionInsp == null) { - return; - } - } - Nodeid nid = Nodeid.fromBinary(nodeid, 0); - if (revisionInsp != null) { - revisionInsp.next(revisionIndex, nid, linkRevIndex); - } - if (parentInsp != null) { - allRevisions[i] = nid; - if (_start > 0) { - // there are chances we don't know parents here, - // postpone parent dispatching for later, now just collect what's missing - firstParentIndexes[i] = parent1RevIndex; - secondParentIndexes[i] = parent2RevIndex; - if (parent1RevIndex < _start && parent1RevIndex >= 0) { - missingParents.put(parent1RevIndex, null); - } - if (parent2RevIndex < _start && parent2RevIndex >= 0) { - missingParents.put(parent2RevIndex, null); - } - } else { - // we iterate from the very beginning, got every index we'll need - Nodeid p1 = parent1RevIndex == -1 ? Nodeid.NULL : allRevisions[parent1RevIndex]; - Nodeid p2 = parent2RevIndex == -1 ? Nodeid.NULL : allRevisions[parent2RevIndex]; - parentInsp.next(revisionIndex, allRevisions[i], parent1RevIndex, parent2RevIndex, p1, p2); - } - i++; - } - } - }); - if (parentInsp != null && _start > 0 ) { - if (missingParents.size() > 0) { - // it's possible to get empty missingParents when _start > 0 e.g. when n-th file revision - // is a copy of another file and hence got -1,-1 parents in this revlog, and we indexWalk(n,n) - for (int k = missingParents.firstKey(), l = missingParents.lastKey(); k <= l; k++) { - // TODO [post-1.1] int[] IntMap#keys() or even sort of iterator that can modify values - if (missingParents.containsKey(k)) { - Nodeid nid = getRepo().getChangelog().getRevision(k); - missingParents.put(k, nid); - } - } - } - - for (int i = 0, revNum = _start; i < allRevisions.length; i++, revNum++) { - int riP1 = firstParentIndexes[i]; - int riP2 = secondParentIndexes[i]; - Nodeid p1, p2; - p1 = p2 = Nodeid.NULL; - if (riP1 >= _start) { - // p1 of revNum's revision is out of iterated range - // (don't check for riP1<end as I assume parents come prior to children in the changelog) - p1 = allRevisions[riP1 - start]; - } else if (riP1 != -1) { - assert riP1 >=0 && riP1 < _start; - p1 = missingParents.get(riP1); - assert p1 != null; - } - // same for Pp2 - if (riP2 >= _start) { - p2 = allRevisions[riP2 - start]; - } else if (riP2 != -1) { - assert riP2 >= 0 && riP2 < _start; - p2 = missingParents.get(riP2); - assert p2 != null; - } - parentInsp.next(revNum, allRevisions[i], riP1, riP2, p1, p2); - } - } + assert head != null; // we know all subclasses of Revlog.Inspector + head.walk(getRepo(), content, _start, end); } - + /** * MARKER */ @@ -577,4 +507,127 @@ } } } + + + private static final class LinkRevDelegate extends RevlogDelegate { + private final LinkRevisionInspector insp; + + public LinkRevDelegate(LinkRevisionInspector inspector, RevlogDelegate next) { + super(next); + assert inspector != null; + insp = inspector; + } + @Override + public void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgRuntimeException { + insp.next(revisionIndex, linkRevision); + super.next(revisionIndex, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeid, data); + } + } + + private static final class RevisionDelegate extends RevlogDelegate { + private final RevisionInspector insp; + public RevisionDelegate(RevisionInspector inspector, RevlogDelegate next) { + super(next); + assert inspector != null; + insp = inspector; + } + @Override + public void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgRuntimeException { + Nodeid nid = getRevision(nodeid); + insp.next(revisionIndex, nid, linkRevision); + super.next(revisionIndex, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, nodeid, data); + } + } + + private static class ParentDelegate extends RevlogDelegate { + private final ParentInspector insp; + private int i = 0; + private final Nodeid[] allRevisions; + // next are to build set of parent indexes that are not part of the range iteration + // i.e. those parents we need to read separately. See Issue 31 for details. + private final int[] firstParentIndexes; + private final int[] secondParentIndexes; + private final IntMap<Nodeid> missingParents; + private final int start; + + public ParentDelegate(ParentInspector inspector, RevlogDelegate next, int _start, int end) { + super(next); + insp = inspector; + start = _start; + allRevisions = new Nodeid[end - _start + 1]; + firstParentIndexes = _start == 0 ? null : new int[allRevisions.length]; + secondParentIndexes = _start == 0 ? null : new int[allRevisions.length]; + missingParents = _start == 0 ? null : new IntMap<Nodeid>(16); + } + @Override + public void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1RevIndex, int parent2RevIndex, byte[] nodeid, DataAccess data) throws HgRuntimeException { + allRevisions[i] = getRevision(nodeid); + if (start > 0) { + // there are chances we don't know parents here, + // postpone parent dispatching for later, now just collect what's missing + firstParentIndexes[i] = parent1RevIndex; + secondParentIndexes[i] = parent2RevIndex; + if (parent1RevIndex < start && parent1RevIndex >= 0) { + missingParents.put(parent1RevIndex, null); + } + if (parent2RevIndex < start && parent2RevIndex >= 0) { + missingParents.put(parent2RevIndex, null); + } + } else { + // we iterate from the very beginning, got every index we'll need + Nodeid p1 = parent1RevIndex == -1 ? Nodeid.NULL : allRevisions[parent1RevIndex]; + Nodeid p2 = parent2RevIndex == -1 ? Nodeid.NULL : allRevisions[parent2RevIndex]; + insp.next(revisionIndex, allRevisions[i], parent1RevIndex, parent2RevIndex, p1, p2); + } + i++; + super.next(revisionIndex, actualLen, baseRevision, linkRevision, parent1RevIndex, parent2RevIndex, nodeid, data); + } + + @Override + protected void postWalk(HgRepository hgRepo) { + if (start > 0) { + complete(hgRepo); + } + super.postWalk(hgRepo); + } + + private void complete(HgRepository repo) { + if (missingParents.size() > 0) { + // it's possible to get empty missingParents when _start > 0 e.g. when n-th file revision + // is a copy of another file and hence got -1,-1 parents in this revlog, and we indexWalk(n,n) + for (int k = missingParents.firstKey(), l = missingParents.lastKey(); k <= l; k++) { + // TODO [post-1.1] int[] IntMap#keys() or even sort of iterator that can modify values + if (missingParents.containsKey(k)) { + Nodeid nid = repo.getChangelog().getRevision(k); + missingParents.put(k, nid); + } + } + } + + for (int i = 0, revNum = start; i < allRevisions.length; i++, revNum++) { + int riP1 = firstParentIndexes[i]; + int riP2 = secondParentIndexes[i]; + Nodeid p1, p2; + p1 = p2 = Nodeid.NULL; + if (riP1 >= start) { + // p1 of revNum's revision is out of iterated range + // (don't check for riP1<end as I assume parents come prior to children in the changelog) + p1 = allRevisions[riP1 - start]; + } else if (riP1 != -1) { + assert riP1 >=0 && riP1 < start; + p1 = missingParents.get(riP1); + assert p1 != null; + } + // same for Pp2 + if (riP2 >= start) { + p2 = allRevisions[riP2 - start]; + } else if (riP2 != -1) { + assert riP2 >= 0 && riP2 < start; + p2 = missingParents.get(riP2); + assert p2 != null; + } + insp.next(revNum, allRevisions[i], riP1, riP2, p1, p2); + } + } + } }