Mercurial > jhg
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 } |
