comparison src/org/tmatesoft/hg/repo/HgRevisionMap.java @ 433:be697c3e951e

Revlog.RevisionMap helper class got promoted as TLC, renamed to HgRevisionMap
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 30 Mar 2012 16:43:09 +0200
parents
children 6526d8adbc0f
comparison
equal deleted inserted replaced
432:1fc0da631200 433:be697c3e951e
1 /*
2 * Copyright (c) 2011-2012 TMate Software Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * For information on how to redistribute this software under
14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@hg4j.com
16 */
17 package org.tmatesoft.hg.repo;
18
19 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION;
20 import static org.tmatesoft.hg.repo.HgRepository.TIP;
21
22 import java.util.Arrays;
23
24 import org.tmatesoft.hg.core.Nodeid;
25 import org.tmatesoft.hg.internal.ArrayHelper;
26 import org.tmatesoft.hg.repo.Revlog.RevisionInspector;
27
28 /**
29 * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of
30 * multiple {@link Revlog#getRevisionIndex(Nodeid)} calls. Rule of thumb is 20+ calls (given
31 * initialization costs). It's also important to take into account memory consumption, for huge
32 * repositories use of this class may pay off only when accessing greatest fraction of all revisions.
33 *
34 * <p>Next code snippet shows instantiation and sample use:
35 * <pre>
36 * RevisionMap<HgChangelog> clogMap = new RevisionMap<HgChangelog>(clog).init();
37 * RevisionMap<HgDataFile> fileMap = new RevisionMap<HgDataFile>(fileNode).init();
38 *
39 * int fileRevIndex = 0;
40 * Nodeid fileRev = fileMap.revision(fileRevIndex);
41 * int csetRevIndex = fileNode.getChangesetRevisionIndex(fileRevIndex);
42 * Nodeid csetRev = clogMap.revision(localCset);
43 * changesetToNodeidMap.put(csetRev, fileRev);
44 * </pre>
45 *
46 * <p>
47 * {@link Revlog#getRevisionIndex(Nodeid)} with straightforward lookup approach performs O(n/2)
48 * <p>
49 * {@link HgRevisionMap#revisionIndex(Nodeid)} is log(n), plus initialization is O(n) (just once).
50 *
51 * @see HgParentChildMap
52 *
53 * @author Artem Tikhomirov
54 * @author TMate Software Ltd.
55 */
56 public final class HgRevisionMap<T extends Revlog> implements RevisionInspector {
57 /*
58 * in fact, initialization is much slower as it instantiates Nodeids, while #getRevisionIndex
59 * compares directly against byte buffer. Measuring cpython with 70k+ gives 3 times difference (47 vs 171)
60 * for complete changelog iteration.
61 */
62
63 /*
64 * XXX 3 * (x * 4) bytes. Can I do better?
65 * It seems, yes. Don't need to keep sorted, always can emulate it with indirect access to sequential through sorted2natural.
66 * i.e. instead sorted[mid].compareTo(toFind), do sequential[sorted2natural[mid]].compareTo(toFind)
67 */
68 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering
69 private Nodeid[] sorted; // for binary search
70 private int[] sorted2natural;
71 private final T revlog;
72
73 public HgRevisionMap(T owner) {
74 revlog = owner;
75 }
76
77 public HgRepository getRepo() {
78 return revlog.getRepo();
79 }
80
81 public void next(int revisionIndex, Nodeid revision, int linkedRevision) {
82 sequential[revisionIndex] = sorted[revisionIndex] = revision;
83 }
84
85 /**
86 * @return <code>this</code> for convenience.
87 */
88 public HgRevisionMap<T> init(/*XXX Pool<Nodeid> to reuse nodeids, if possible. */) throws HgInvalidControlFileException{
89 // XXX HgRepository.register((RepoChangeListener) this); // listen to changes in repo, re-init if needed?
90 final int revisionCount = revlog.getRevisionCount();
91 sequential = new Nodeid[revisionCount];
92 sorted = new Nodeid[revisionCount];
93 revlog.indexWalk(0, TIP, this);
94 // next is alternative to Arrays.sort(sorted), and build sorted2natural looking up each element of sequential in sorted.
95 // the way sorted2natural was build is O(n*log n).
96 final ArrayHelper ah = new ArrayHelper();
97 ah.sort(sorted);
98 // note, values in ArrayHelper#getReversed are 1-based indexes, not 0-based
99 sorted2natural = ah.getReverse();
100 return this;
101 }
102
103 public Nodeid revision(int revisionIndex) {
104 return sequential[revisionIndex];
105 }
106 public int revisionIndex(Nodeid revision) {
107 if (revision == null || revision.isNull()) {
108 return BAD_REVISION;
109 }
110 int x = Arrays.binarySearch(sorted, revision);
111 if (x < 0) {
112 return BAD_REVISION;
113 }
114 return sorted2natural[x]-1;
115 }
116 }