comparison src/org/tmatesoft/hg/repo/HgParentChildMap.java @ 657:6334b0267103

ParentChildMap can supply RevisionMap. Refactor ArrayHelper to keep most of sorted/reverse index magic inside
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 04 Jul 2013 20:27:45 +0200
parents a937e63b6e02
children af5223b86dd3
comparison
equal deleted inserted replaced
656:a937e63b6e02 657:6334b0267103
26 import java.util.HashSet; 26 import java.util.HashSet;
27 import java.util.LinkedList; 27 import java.util.LinkedList;
28 import java.util.List; 28 import java.util.List;
29 29
30 import org.tmatesoft.hg.core.Nodeid; 30 import org.tmatesoft.hg.core.Nodeid;
31 import org.tmatesoft.hg.internal.ArrayHelper;
31 import org.tmatesoft.hg.internal.IntMap; 32 import org.tmatesoft.hg.internal.IntMap;
32 import org.tmatesoft.hg.repo.Revlog.ParentInspector; 33 import org.tmatesoft.hg.repo.Revlog.ParentInspector;
33 34
34 /** 35 /**
35 * Helper class to deal with parent-child relationship between revisions <i>en masse</i>. 36 * Helper class to deal with parent-child relationship between revisions <i>en masse</i>.
60 */ 61 */
61 public final class HgParentChildMap<T extends Revlog> implements ParentInspector { 62 public final class HgParentChildMap<T extends Revlog> implements ParentInspector {
62 63
63 // IMPORTANT: Nodeid instances shall be shared between all arrays 64 // IMPORTANT: Nodeid instances shall be shared between all arrays
64 65
66 private final T revlog;
65 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering 67 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering
66 private Nodeid[] sorted; // for binary search 68 private Nodeid[] sorted; // for binary search, just an origin of the actual value in use, the one inside seqWrapper
67 private int[] sorted2natural; // indexes in sorted to indexes in sequential
68 private Nodeid[] firstParent; // parents by natural order (i.e. firstParent[A] is parent of revision with index A) 69 private Nodeid[] firstParent; // parents by natural order (i.e. firstParent[A] is parent of revision with index A)
69 private Nodeid[] secondParent; 70 private Nodeid[] secondParent;
70 private final T revlog;
71 private IntMap<Nodeid> heads; 71 private IntMap<Nodeid> heads;
72 private BitSet headsBitSet; // 1 indicates revision got children, != null only during init; 72 private BitSet headsBitSet; // 1 indicates revision got children, != null only during init;
73 private HgRevisionMap<T> revisionIndexMap;
74 private ArrayHelper<Nodeid> seqWrapper;
73 75
74 76
75 public HgParentChildMap(T owner) { 77 public HgParentChildMap(T owner) {
76 revlog = owner; 78 revlog = owner;
77 } 79 }
109 // real improvement (IntMap has 2n capacity, and element lookup is log(n) instead of array's constant). 111 // real improvement (IntMap has 2n capacity, and element lookup is log(n) instead of array's constant).
110 // FWIW: in cpython's repo, with 70k+ revisions, there are 2618 values in secondParent 112 // FWIW: in cpython's repo, with 70k+ revisions, there are 2618 values in secondParent
111 secondParent = new Nodeid[revisionCount]; 113 secondParent = new Nodeid[revisionCount];
112 // 114 //
113 sequential = new Nodeid[revisionCount]; 115 sequential = new Nodeid[revisionCount];
114 sorted = new Nodeid[revisionCount]; 116 sorted = new Nodeid[revisionCount];
115 headsBitSet = new BitSet(revisionCount); 117 headsBitSet = new BitSet(revisionCount);
116 revlog.indexWalk(0, TIP, this); 118 revlog.indexWalk(0, TIP, this);
117 Arrays.sort(sorted); 119 seqWrapper = new ArrayHelper<Nodeid>(sequential);
118 // FIXME use ArrayHelper instead 120 // HgRevisionMap doesn't keep sorted, try alternative here.
119 sorted2natural = new int[revisionCount]; 121 // reference this.sorted (not only from ArrayHelper) helps to track ownership in hprof/mem dumps
120 for (int i = 0; i < revisionCount; i++) { 122 seqWrapper.sort(sorted, false, true);
121 Nodeid n = sequential[i];
122 int x = Arrays.binarySearch(sorted, n);
123 assertSortedIndex(x);
124 sorted2natural[x] = i;
125 }
126 // no reason to keep BitSet, number of heads is usually small 123 // no reason to keep BitSet, number of heads is usually small
127 IntMap<Nodeid> _heads = new IntMap<Nodeid>(headsBitSet.size() - headsBitSet.cardinality()); 124 IntMap<Nodeid> _heads = new IntMap<Nodeid>(headsBitSet.size() - headsBitSet.cardinality());
128 int index = 0; 125 int index = 0;
129 while (index < sequential.length) { 126 while (index < sequential.length) {
130 index = headsBitSet.nextClearBit(index); 127 index = headsBitSet.nextClearBit(index);
149 * Note, {@link Nodeid#NULL}, although implicitly present as parent of a first revision, is not recognized as known. 146 * Note, {@link Nodeid#NULL}, although implicitly present as parent of a first revision, is not recognized as known.
150 * @param nid revision to check, not <code>null</code> 147 * @param nid revision to check, not <code>null</code>
151 * @return <code>true</code> if revision matches any revision in this revlog 148 * @return <code>true</code> if revision matches any revision in this revlog
152 */ 149 */
153 public boolean knownNode(Nodeid nid) { 150 public boolean knownNode(Nodeid nid) {
154 return Arrays.binarySearch(sorted, nid) >= 0; 151 return seqWrapper.binarySearchSorted(nid) >= 0;
155 } 152 }
156 153
157 /** 154 /**
158 * null if none. only known nodes (as per #knownNode) are accepted as arguments 155 * null if none. only known nodes (as per #knownNode) are accepted as arguments
159 */ 156 */
160 public Nodeid firstParent(Nodeid nid) { 157 public Nodeid firstParent(Nodeid nid) {
161 int x = Arrays.binarySearch(sorted, nid); 158 int x = seqWrapper.binarySearchSorted(nid);
162 assertSortedIndex(x); 159 assertSortedIndex(x);
163 int i = sorted2natural[x]; 160 int i = seqWrapper.getReverseIndex(x);
164 return firstParent[i]; 161 return firstParent[i];
165 } 162 }
166 163
167 // never null, Nodeid.NULL if none known 164 // never null, Nodeid.NULL if none known
168 public Nodeid safeFirstParent(Nodeid nid) { 165 public Nodeid safeFirstParent(Nodeid nid) {
169 Nodeid rv = firstParent(nid); 166 Nodeid rv = firstParent(nid);
170 return rv == null ? Nodeid.NULL : rv; 167 return rv == null ? Nodeid.NULL : rv;
171 } 168 }
172 169
173 public Nodeid secondParent(Nodeid nid) { 170 public Nodeid secondParent(Nodeid nid) {
174 int x = Arrays.binarySearch(sorted, nid); 171 int x = seqWrapper.binarySearchSorted(nid);
175 assertSortedIndex(x); 172 assertSortedIndex(x);
176 int i = sorted2natural[x]; 173 int i = seqWrapper.getReverseIndex(x);
177 return secondParent[i]; 174 return secondParent[i];
178 } 175 }
179 176
180 public Nodeid safeSecondParent(Nodeid nid) { 177 public Nodeid safeSecondParent(Nodeid nid) {
181 Nodeid rv = secondParent(nid); 178 Nodeid rv = secondParent(nid);
182 return rv == null ? Nodeid.NULL : rv; 179 return rv == null ? Nodeid.NULL : rv;
183 } 180 }
184 181
185 public boolean appendParentsOf(Nodeid nid, Collection<Nodeid> c) { 182 public boolean appendParentsOf(Nodeid nid, Collection<Nodeid> c) {
186 int x = Arrays.binarySearch(sorted, nid); 183 int x = seqWrapper.binarySearchSorted(nid);
187 assertSortedIndex(x); 184 assertSortedIndex(x);
188 int i = sorted2natural[x]; 185 int i = seqWrapper.getReverseIndex(x);
189 Nodeid p1 = firstParent[i]; 186 Nodeid p1 = firstParent[i];
190 boolean modified = false; 187 boolean modified = false;
191 if (p1 != null) { 188 if (p1 != null) {
192 modified = c.add(p1); 189 modified = c.add(p1);
193 } 190 }
212 int earliestRevision = Integer.MAX_VALUE; 209 int earliestRevision = Integer.MAX_VALUE;
213 assert sequential.length == firstParent.length && firstParent.length == secondParent.length; 210 assert sequential.length == firstParent.length && firstParent.length == secondParent.length;
214 // first, find earliest index of roots in question, as there's no sense 211 // first, find earliest index of roots in question, as there's no sense
215 // to check children among nodes prior to branch's root node 212 // to check children among nodes prior to branch's root node
216 for (Nodeid r : roots) { 213 for (Nodeid r : roots) {
217 int x = Arrays.binarySearch(sorted, r); 214 int x = seqWrapper.binarySearchSorted(r);
218 assertSortedIndex(x); 215 assertSortedIndex(x);
219 int i = sorted2natural[x]; 216 int i = seqWrapper.getReverseIndex(x);
220 if (i < earliestRevision) { 217 if (i < earliestRevision) {
221 earliestRevision = i; 218 earliestRevision = i;
222 } 219 }
223 parents.add(sequential[i]); // add canonical instance in hope equals() is bit faster when can do a == 220 parents.add(sequential[i]); // add canonical instance in hope equals() is bit faster when can do a ==
224 } 221 }
229 } 226 }
230 } 227 }
231 return result; 228 return result;
232 } 229 }
233 230
234 public long AAA = 0;
235
236 /** 231 /**
237 * @return revisions that have supplied revision as their immediate parent 232 * @return revisions that have supplied revision as their immediate parent
238 */ 233 */
239 public List<Nodeid> directChildren(Nodeid nid) { 234 public List<Nodeid> directChildren(Nodeid nid) {
240 int x = Arrays.binarySearch(sorted, nid); 235 int x = seqWrapper.binarySearchSorted(nid);
241 assertSortedIndex(x); 236 assertSortedIndex(x);
242 nid = sorted[x]; // canonical instance 237 int start = seqWrapper.getReverseIndex(x);
243 int start = sorted2natural[x]; 238 nid = sequential[start]; // canonical instance
244 if (!hasChildren(start)) { 239 if (!hasChildren(start)) {
245 return Collections.emptyList(); 240 return Collections.emptyList();
246 } 241 }
247 ArrayList<Nodeid> result = new ArrayList<Nodeid>(5); 242 ArrayList<Nodeid> result = new ArrayList<Nodeid>(5);
248 for (int i = start + 1; i < sequential.length; i++) { 243 for (int i = start + 1; i < sequential.length; i++) {
249 AAA++;
250 if (nid == firstParent[i] || nid == secondParent[i]) { 244 if (nid == firstParent[i] || nid == secondParent[i]) {
251 result.add(sequential[i]); 245 result.add(sequential[i]);
252 } 246 }
253 } 247 }
254 return result; 248 return result;
257 /** 251 /**
258 * @param nid possibly parent node, shall be {@link #knownNode(Nodeid) known} in this revlog. 252 * @param nid possibly parent node, shall be {@link #knownNode(Nodeid) known} in this revlog.
259 * @return <code>true</code> if there's any node in this revlog that has specified node as one of its parents. 253 * @return <code>true</code> if there's any node in this revlog that has specified node as one of its parents.
260 */ 254 */
261 public boolean hasChildren(Nodeid nid) { 255 public boolean hasChildren(Nodeid nid) {
262 int x = Arrays.binarySearch(sorted, nid); 256 int x = seqWrapper.binarySearchSorted(nid);
263 assertSortedIndex(x); 257 assertSortedIndex(x);
264 int i = sorted2natural[x]; 258 int i = seqWrapper.getReverseIndex(x);
265 return hasChildren(i); 259 return hasChildren(i);
266 } 260 }
267 261
268 /** 262 /**
269 * @return all revisions this map knows about 263 * @return all revisions this map knows about
278 * @param root revision to check for being (grand-)*parent of a child 272 * @param root revision to check for being (grand-)*parent of a child
279 * @param wannaBeChild candidate descendant revision 273 * @param wannaBeChild candidate descendant revision
280 * @return <code>true</code> if <code>wannaBeChild</code> is among children of <code>root</code> 274 * @return <code>true</code> if <code>wannaBeChild</code> is among children of <code>root</code>
281 */ 275 */
282 public boolean isChild(Nodeid root, Nodeid wannaBeChild) { 276 public boolean isChild(Nodeid root, Nodeid wannaBeChild) {
283 int x = Arrays.binarySearch(sorted, root); 277 int x = seqWrapper.binarySearchSorted(root);
284 assertSortedIndex(x); 278 assertSortedIndex(x);
285 root = sorted[x]; // canonical instance 279 final int start = seqWrapper.getReverseIndex(x);
286 final int start = sorted2natural[x]; 280 root = sequential[start]; // canonical instance
287 if (!hasChildren(start)) { 281 if (!hasChildren(start)) {
288 return false; // root got no children at all 282 return false; // root got no children at all
289 } 283 }
290 int y = Arrays.binarySearch(sorted, wannaBeChild); 284 int y = seqWrapper.binarySearchSorted(wannaBeChild);
291 if (y < 0) { 285 if (y < 0) {
292 return false; // not found 286 return false; // not found
293 } 287 }
294 wannaBeChild = sorted[y]; // canonicalize 288 final int end = seqWrapper.getReverseIndex(y);
295 final int end = sorted2natural[y]; 289 wannaBeChild = sequential[end]; // canonicalize
296 if (end <= start) { 290 if (end <= start) {
297 return false; // potential child was in repository earlier than root 291 return false; // potential child was in repository earlier than root
298 } 292 }
299 HashSet<Nodeid> parents = new HashSet<Nodeid>(); 293 HashSet<Nodeid> parents = new HashSet<Nodeid>();
300 parents.add(root); 294 parents.add(root);
310 * @return elements of this map that do not have a child recorded therein. 304 * @return elements of this map that do not have a child recorded therein.
311 */ 305 */
312 public Collection<Nodeid> heads() { 306 public Collection<Nodeid> heads() {
313 return heads.values(); 307 return heads.values();
314 } 308 }
309
310 /**
311 * @return map of revision to indexes
312 */
313 public HgRevisionMap<T> getRevisionMap() {
314 if (revisionIndexMap == null) {
315 revisionIndexMap = new HgRevisionMap<T>(revlog);
316 revisionIndexMap.init(seqWrapper);
317 }
318 return revisionIndexMap;
319 }
315 320
316 private boolean hasChildren(int sequentialIndex) { 321 private boolean hasChildren(int sequentialIndex) {
317 return !heads.containsKey(sequentialIndex); 322 return !heads.containsKey(sequentialIndex);
318 } 323 }
319 } 324 }