Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/Revlog.java @ 432:1fc0da631200
Revlog.ParentWalker helper class got promoted as TLC, renamed to HgParentChildMap
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Fri, 30 Mar 2012 16:22:51 +0200 |
parents | 12f668401613 |
children | be697c3e951e |
comparison
equal
deleted
inserted
replaced
431:12f668401613 | 432:1fc0da631200 |
---|---|
20 | 20 |
21 import java.io.IOException; | 21 import java.io.IOException; |
22 import java.nio.ByteBuffer; | 22 import java.nio.ByteBuffer; |
23 import java.util.ArrayList; | 23 import java.util.ArrayList; |
24 import java.util.Arrays; | 24 import java.util.Arrays; |
25 import java.util.Collection; | |
26 import java.util.HashSet; | |
27 import java.util.LinkedList; | |
28 import java.util.List; | 25 import java.util.List; |
29 | 26 |
30 import org.tmatesoft.hg.core.Nodeid; | 27 import org.tmatesoft.hg.core.Nodeid; |
31 import org.tmatesoft.hg.internal.ArrayHelper; | 28 import org.tmatesoft.hg.internal.ArrayHelper; |
32 import org.tmatesoft.hg.internal.DataAccess; | 29 import org.tmatesoft.hg.internal.DataAccess; |
335 public interface ParentInspector extends Inspector { | 332 public interface ParentInspector extends Inspector { |
336 // XXX document whether parentX is -1 or a constant (BAD_REVISION? or dedicated?) | 333 // XXX document whether parentX is -1 or a constant (BAD_REVISION? or dedicated?) |
337 void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2); | 334 void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2); |
338 } | 335 } |
339 | 336 |
340 /* | 337 protected HgParentChildMap<? extends Revlog> getParentWalker() { |
341 * FIXME think over if it's better to do either: | 338 HgParentChildMap<Revlog> pw = new HgParentChildMap<Revlog>(this); |
342 * pw = getChangelog().new ParentWalker(); pw.init() and pass pw instance around as needed | 339 pw.init(); |
343 * or | 340 return pw; |
344 * add Revlog#getParentWalker(), static class, make cons() and #init package-local, and keep SoftReference to allow walker reuse. | |
345 * | |
346 * and yes, walker is not a proper name | |
347 */ | |
348 public final class ParentWalker implements ParentInspector { | |
349 | |
350 | |
351 private Nodeid[] sequential; // natural repository order, childrenOf rely on ordering | |
352 private Nodeid[] sorted; // for binary search | |
353 private int[] sorted2natural; | |
354 private Nodeid[] firstParent; | |
355 private Nodeid[] secondParent; | |
356 | |
357 // Nodeid instances shall be shared between all arrays | |
358 | |
359 public ParentWalker() { | |
360 } | |
361 | |
362 public HgRepository getRepo() { | |
363 return Revlog.this.getRepo(); | |
364 } | |
365 | |
366 public void next(int revisionNumber, Nodeid revision, int parent1Revision, int parent2Revision, Nodeid nidParent1, Nodeid nidParent2) { | |
367 if (parent1Revision >= revisionNumber || parent2Revision >= revisionNumber) { | |
368 throw new IllegalStateException(); // sanity, revisions are sequential | |
369 } | |
370 int ix = revisionNumber; | |
371 sequential[ix] = sorted[ix] = revision; | |
372 if (parent1Revision != -1) { | |
373 firstParent[ix] = sequential[parent1Revision]; | |
374 } | |
375 if (parent2Revision != -1) { // revlog of DataAccess.java has p2 set when p1 is -1 | |
376 secondParent[ix] = sequential[parent2Revision]; | |
377 } | |
378 } | |
379 | |
380 public void init() throws HgInvalidControlFileException { | |
381 final int revisionCount = Revlog.this.getRevisionCount(); | |
382 firstParent = new Nodeid[revisionCount]; | |
383 // TODO [post 1.0] Branches/merges are less frequent, and most of secondParent would be -1/null, hence | |
384 // IntMap might be better alternative here, but need to carefully analyze (test) whether this brings | |
385 // real improvement (IntMap has 2n capacity, and element lookup is log(n) instead of array's constant) | |
386 secondParent = new Nodeid[revisionCount]; | |
387 // | |
388 sequential = new Nodeid[revisionCount]; | |
389 sorted = new Nodeid[revisionCount]; | |
390 Revlog.this.indexWalk(0, TIP, this); | |
391 Arrays.sort(sorted); | |
392 sorted2natural = new int[revisionCount]; | |
393 for (int i = 0; i < revisionCount; i++) { | |
394 Nodeid n = sequential[i]; | |
395 int x = Arrays.binarySearch(sorted, n); | |
396 assertSortedIndex(x); | |
397 sorted2natural[x] = i; | |
398 } | |
399 } | |
400 | |
401 private void assertSortedIndex(int x) { | |
402 if (x < 0) { | |
403 throw new HgInvalidStateException(String.format("Bad index", x)); | |
404 } | |
405 } | |
406 | |
407 /** | |
408 * Tells whether supplied revision is from the walker's associated revlog. | |
409 * Note, {@link Nodeid#NULL}, although implicitly present as parent of a first revision, is not recognized as known. | |
410 * @param nid revision to check, not <code>null</code> | |
411 * @return <code>true</code> if revision matches any revision in this revlog | |
412 */ | |
413 public boolean knownNode(Nodeid nid) { | |
414 return Arrays.binarySearch(sorted, nid) >= 0; | |
415 } | |
416 | |
417 /** | |
418 * null if none. only known nodes (as per #knownNode) are accepted as arguments | |
419 */ | |
420 public Nodeid firstParent(Nodeid nid) { | |
421 int x = Arrays.binarySearch(sorted, nid); | |
422 assertSortedIndex(x); | |
423 int i = sorted2natural[x]; | |
424 return firstParent[i]; | |
425 } | |
426 | |
427 // never null, Nodeid.NULL if none known | |
428 public Nodeid safeFirstParent(Nodeid nid) { | |
429 Nodeid rv = firstParent(nid); | |
430 return rv == null ? Nodeid.NULL : rv; | |
431 } | |
432 | |
433 public Nodeid secondParent(Nodeid nid) { | |
434 int x = Arrays.binarySearch(sorted, nid); | |
435 assertSortedIndex(x); | |
436 int i = sorted2natural[x]; | |
437 return secondParent[i]; | |
438 } | |
439 | |
440 public Nodeid safeSecondParent(Nodeid nid) { | |
441 Nodeid rv = secondParent(nid); | |
442 return rv == null ? Nodeid.NULL : rv; | |
443 } | |
444 | |
445 public boolean appendParentsOf(Nodeid nid, Collection<Nodeid> c) { | |
446 int x = Arrays.binarySearch(sorted, nid); | |
447 assertSortedIndex(x); | |
448 int i = sorted2natural[x]; | |
449 Nodeid p1 = firstParent[i]; | |
450 boolean modified = false; | |
451 if (p1 != null) { | |
452 modified = c.add(p1); | |
453 } | |
454 Nodeid p2 = secondParent[i]; | |
455 if (p2 != null) { | |
456 modified = c.add(p2) || modified; | |
457 } | |
458 return modified; | |
459 } | |
460 | |
461 // XXX alternative (and perhaps more reliable) approach would be to make a copy of allNodes and remove | |
462 // nodes, their parents and so on. | |
463 | |
464 // @return ordered collection of all children rooted at supplied nodes. Nodes shall not be descendants of each other! | |
465 // Nodeids shall belong to this revlog | |
466 public List<Nodeid> childrenOf(List<Nodeid> roots) { | |
467 HashSet<Nodeid> parents = new HashSet<Nodeid>(); | |
468 LinkedList<Nodeid> result = new LinkedList<Nodeid>(); | |
469 int earliestRevision = Integer.MAX_VALUE; | |
470 assert sequential.length == firstParent.length && firstParent.length == secondParent.length; | |
471 // first, find earliest index of roots in question, as there's no sense | |
472 // to check children among nodes prior to branch's root node | |
473 for (Nodeid r : roots) { | |
474 int x = Arrays.binarySearch(sorted, r); | |
475 assertSortedIndex(x); | |
476 int i = sorted2natural[x]; | |
477 if (i < earliestRevision) { | |
478 earliestRevision = i; | |
479 } | |
480 parents.add(sequential[i]); // add canonical instance in hope equals() is bit faster when can do a == | |
481 } | |
482 for (int i = earliestRevision + 1; i < sequential.length; i++) { | |
483 if (parents.contains(firstParent[i]) || parents.contains(secondParent[i])) { | |
484 parents.add(sequential[i]); // to find next child | |
485 result.add(sequential[i]); | |
486 } | |
487 } | |
488 return result; | |
489 } | |
490 | |
491 /** | |
492 * @return revisions that have supplied revision as their immediate parent | |
493 */ | |
494 public List<Nodeid> directChildren(Nodeid nid) { | |
495 LinkedList<Nodeid> result = new LinkedList<Nodeid>(); | |
496 int x = Arrays.binarySearch(sorted, nid); | |
497 assertSortedIndex(x); | |
498 nid = sorted[x]; // canonical instance | |
499 int start = sorted2natural[x]; | |
500 for (int i = start + 1; i < sequential.length; i++) { | |
501 if (nid == firstParent[i] || nid == secondParent[i]) { | |
502 result.add(sequential[i]); | |
503 } | |
504 } | |
505 return result; | |
506 } | |
507 | |
508 /** | |
509 * @param nid possibly parent node, shall be {@link #knownNode(Nodeid) known} in this revlog. | |
510 * @return <code>true</code> if there's any node in this revlog that has specified node as one of its parents. | |
511 */ | |
512 public boolean hasChildren(Nodeid nid) { | |
513 int x = Arrays.binarySearch(sorted, nid); | |
514 assertSortedIndex(x); | |
515 int i = sorted2natural[x]; | |
516 assert firstParent.length == secondParent.length; // just in case later I implement sparse array for secondParent | |
517 assert firstParent.length == sequential.length; | |
518 // to use == instead of equals, take the same Nodeid instance we used to fill all the arrays. | |
519 final Nodeid canonicalNode = sequential[i]; | |
520 i++; // no need to check node itself. child nodes may appear in sequential only after revision in question | |
521 for (; i < sequential.length; i++) { | |
522 // TODO [post 1.0] likely, not very effective. | |
523 // May want to optimize it with another (Tree|Hash)Set, created on demand on first use, | |
524 // however, need to be careful with memory usage | |
525 if (firstParent[i] == canonicalNode || secondParent[i] == canonicalNode) { | |
526 return true; | |
527 } | |
528 } | |
529 return false; | |
530 } | |
531 } | 341 } |
532 | 342 |
533 /** | 343 /** |
534 * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of | 344 * Effective int to Nodeid and vice versa translation. It's advised to use this class instead of |
535 * multiple {@link Revlog#getRevisionIndex(Nodeid)} calls. | 345 * multiple {@link Revlog#getRevisionIndex(Nodeid)} calls. |