view src/org/tmatesoft/hg/repo/HgRevisionMap.java @ 709:497e697636fc

Report merged lines as changed block if possible, not as a sequence of added/deleted blocks. To facilitate access to merge parent lines AddBlock got mergeLineAt() method that reports index of the line in the second parent (if any), while insertedAt() has been changed to report index in the first parent always
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 21 Aug 2013 16:23:27 +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);
	}
}