Mercurial > jhg
view src/org/tmatesoft/hg/repo/HgRevisionMap.java @ 695:053bb4397bf9
Refactoring: nice Revlog.indexWalk() implementation
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Mon, 05 Aug 2013 18:45:16 +0200 |
parents | 6334b0267103 |
children |
line wrap: on
line source
/* * Copyright (c) 2011-2012 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.repo; import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; import static org.tmatesoft.hg.repo.HgRepository.TIP; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.internal.ArrayHelper; import org.tmatesoft.hg.repo.Revlog.RevisionInspector; /** * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of * multiple {@link Revlog#getRevisionIndex(Nodeid)} calls. Rule of thumb is 20+ calls (given * initialization costs). It's also important to take into account memory consumption, for huge * repositories use of this class may pay off only when accessing greatest fraction of all revisions. * * <p>Next code snippet shows instantiation and sample use: * <pre> * RevisionMap<HgChangelog> clogMap = new RevisionMap<HgChangelog>(clog).init(); * RevisionMap<HgDataFile> fileMap = new RevisionMap<HgDataFile>(fileNode).init(); * * int fileRevIndex = 0; * Nodeid fileRev = fileMap.revision(fileRevIndex); * int csetRevIndex = fileNode.getChangesetRevisionIndex(fileRevIndex); * Nodeid csetRev = clogMap.revision(localCset); * changesetToNodeidMap.put(csetRev, fileRev); * </pre> * * <p> * {@link Revlog#getRevisionIndex(Nodeid)} with straightforward lookup approach performs O(n/2) * <p> * {@link HgRevisionMap#revisionIndex(Nodeid)} is log(n), plus initialization is O(n) (just once). * * @see HgParentChildMap * * @author Artem Tikhomirov * @author TMate Software Ltd. */ public final class HgRevisionMap<T extends Revlog> implements RevisionInspector { /* * in fact, initialization is much slower as it instantiates Nodeids, while #getRevisionIndex * compares directly against byte buffer. Measuring cpython with 70k+ gives 3 times difference (47 vs 171) * for complete changelog iteration. */ private final T revlog; /* * 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 private ArrayHelper<Nodeid> seqWrapper; public HgRevisionMap(T owner) { revlog = owner; } public HgRepository getRepo() { return revlog.getRepo(); } public void next(int revisionIndex, Nodeid revision, int linkedRevision) { sequential[revisionIndex] = revision; } /** * @return <code>this</code> for convenience. */ public HgRevisionMap<T> init(/*XXX Pool<Nodeid> to reuse nodeids, if possible. */) throws HgRuntimeException { // XXX HgRepository.register((RepoChangeListener) this); // listen to changes in repo, re-init if needed? final int revisionCount = revlog.getRevisionCount(); sequential = new Nodeid[revisionCount]; revlog.indexWalk(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). seqWrapper = new ArrayHelper<Nodeid>(sequential); seqWrapper.sort(null, true, false); return this; } /* friendly initializer to use from HgParentChildMap /*package*/ void init(ArrayHelper<Nodeid> _seqWrapper) { assert _seqWrapper.getData().length == revlog.getRevisionCount(); sequential = _seqWrapper.getData(); seqWrapper = _seqWrapper; } public Nodeid revision(int revisionIndex) { return sequential[revisionIndex]; } public int revisionIndex(Nodeid revision) { if (revision == null || revision.isNull()) { return BAD_REVISION; } return seqWrapper.binarySearch(revision, BAD_REVISION); } }